/*-------------------------------------------------------------------------
 *
 * arrayfuncs.c
 *	  数组的支持函数。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/utils/adt/arrayfuncs.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include <ctype.h>
#include <math.h>

#include "access/htup_details.h"
#include "catalog/pg_type.h"
#include "common/int.h"
#include "funcapi.h"
#include "libpq/pqformat.h"
#include "nodes/nodeFuncs.h"
#include "nodes/supportnodes.h"
#include "optimizer/optimizer.h"
#include "port/pg_bitutils.h"
#include "utils/array.h"
#include "utils/arrayaccess.h"
#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/selfuncs.h"
#include "utils/typcache.h"


/*
 * GUC参数
 */
bool		Array_nulls = true;

/*
 * 本地定义
 */
#define ASSGN	 "="

#define AARR_FREE_IF_COPY(array,n) \
	do { \
		if (!VARATT_IS_EXPANDED_HEADER(array)) \
			PG_FREE_IF_COPY(array, n); \
	} while (0)

typedef enum
{
	ARRAY_NO_LEVEL,
	ARRAY_LEVEL_STARTED,
	ARRAY_ELEM_STARTED,
	ARRAY_ELEM_COMPLETED,
	ARRAY_QUOTED_ELEM_STARTED,
	ARRAY_QUOTED_ELEM_COMPLETED,
	ARRAY_ELEM_DELIMITED,
	ARRAY_LEVEL_COMPLETED,
	ARRAY_LEVEL_DELIMITED
} ArrayParseState;

/* array_iterate()的工作状态 */
typedef struct ArrayIteratorData
{
	/* 关于数组的基本信息，在array_create_iterator()期间设置 */
	ArrayType  *arr;			/* 我们正在迭代的数组 */
	bits8	   *nullbitmap;		/* 其空位 bitmap（如果有） */
	int			nitems;			/* 数组中元素的总数量 */
	int16		typlen;			/* 元素类型的长度 */
	bool		typbyval;		/* 元素类型的byval属性 */
	char		typalign;		/* 元素类型的对齐属性 */

	/* 关于请求的切片大小的信息 */
	int			slice_ndim;		/* 切片维度，若没有切片则为0 */
	int			slice_len;		/* 每个切片的元素数量 */
	int		   *slice_dims;		/* 切片维度数组 */
	int		   *slice_lbound;	/* 切片下界数组 */
	Datum	   *slice_values;	/* 长度为slice_len的工作区 */
	bool	   *slice_nulls;	/* 长度为slice_len的工作区 */

	/* 当前位置信息，在每次迭代时更新 */
	char	   *data_ptr;		/* 我们在数组中的当前位置 */
	int			current_item;	/* 我们在数组中的项目编号 */
}			ArrayIteratorData;

static bool fc_array_isspace(char fc_ch);
static int	fc_ArrayCount(const char *fc_str, int *fc_dim, char fc_typdelim);
static void fc_ReadArrayStr(char *fc_arrayStr, const char *fc_origStr,
						 int fc_nitems, int fc_ndim, int *fc_dim,
						 FmgrInfo *fc_inputproc, Oid fc_typioparam, int32 fc_typmod,
						 char fc_typdelim,
						 int fc_typlen, bool fc_typbyval, char fc_typalign,
						 Datum *fc_values, bool *fc_nulls,
						 bool *fc_hasnulls, int32 *fc_nbytes);
static void fc_ReadArrayBinary(StringInfo fc_buf, int fc_nitems,
							FmgrInfo *fc_receiveproc, Oid fc_typioparam, int32 fc_typmod,
							int fc_typlen, bool fc_typbyval, char fc_typalign,
							Datum *fc_values, bool *fc_nulls,
							bool *fc_hasnulls, int32 *fc_nbytes);
static Datum fc_array_get_element_expanded(Datum fc_arraydatum,
										int fc_nSubscripts, int *fc_indx,
										int fc_arraytyplen,
										int fc_elmlen, bool fc_elmbyval, char fc_elmalign,
										bool *fc_isNull);
static Datum fc_array_set_element_expanded(Datum fc_arraydatum,
										int fc_nSubscripts, int *fc_indx,
										Datum fc_dataValue, bool fc_isNull,
										int fc_arraytyplen,
										int fc_elmlen, bool fc_elmbyval, char fc_elmalign);
static bool fc_array_get_isnull(const bits8 *fc_nullbitmap, int fc_offset);
static void fc_array_set_isnull(bits8 *fc_nullbitmap, int fc_offset, bool fc_isNull);
static Datum fc_ArrayCast(char *fc_value, bool fc_byval, int fc_len);
static int	fc_ArrayCastAndSet(Datum fc_src,
							int fc_typlen, bool fc_typbyval, char fc_typalign,
							char *fc_dest);
static char *fc_array_seek(char *fc_ptr, int fc_offset, bits8 *fc_nullbitmap, int fc_nitems,
						int fc_typlen, bool fc_typbyval, char fc_typalign);
static int	fc_array_nelems_size(char *fc_ptr, int fc_offset, bits8 *fc_nullbitmap,
							  int fc_nitems, int fc_typlen, bool fc_typbyval, char fc_typalign);
static int	fc_array_copy(char *fc_destptr, int fc_nitems,
					   char *fc_srcptr, int fc_offset, bits8 *fc_nullbitmap,
					   int fc_typlen, bool fc_typbyval, char fc_typalign);
static int	fc_array_slice_size(char *fc_arraydataptr, bits8 *fc_arraynullsptr,
							 int fc_ndim, int *fc_dim, int *fc_lb,
							 int *fc_st, int *fc_endp,
							 int fc_typlen, bool fc_typbyval, char fc_typalign);
static void fc_array_extract_slice(ArrayType *fc_newarray,
								int fc_ndim, int *fc_dim, int *fc_lb,
								char *fc_arraydataptr, bits8 *fc_arraynullsptr,
								int *fc_st, int *fc_endp,
								int fc_typlen, bool fc_typbyval, char fc_typalign);
static void fc_array_insert_slice(ArrayType *fc_destArray, ArrayType *fc_origArray,
							   ArrayType *fc_srcArray,
							   int fc_ndim, int *fc_dim, int *fc_lb,
							   int *fc_st, int *fc_endp,
							   int fc_typlen, bool fc_typbyval, char fc_typalign);
static int	fc_array_cmp(FunctionCallInfo fcinfo);
static ArrayType *fc_create_array_envelope(int fc_ndims, int *fc_dimv, int *fc_lbsv, int fc_nbytes,
										Oid fc_elmtype, int fc_dataoffset);
static ArrayType *fc_array_fill_internal(ArrayType *fc_dims, ArrayType *fc_lbs,
									  Datum fc_value, bool fc_isnull, Oid fc_elmtype,
									  FunctionCallInfo fcinfo);
static ArrayType *fc_array_replace_internal(ArrayType *fc_array,
										 Datum fc_search, bool fc_search_isnull,
										 Datum fc_replace, bool fc_replace_isnull,
										 bool fc_remove, Oid fc_collation,
										 FunctionCallInfo fcinfo);
static int	fc_width_bucket_array_float8(Datum fc_operand, ArrayType *fc_thresholds);
static int	fc_width_bucket_array_fixed(Datum fc_operand,
									 ArrayType *fc_thresholds,
									 Oid fc_collation,
									 TypeCacheEntry *fc_typentry);
static int	fc_width_bucket_array_variable(Datum fc_operand,
										ArrayType *fc_thresholds,
										Oid fc_collation,
										TypeCacheEntry *fc_typentry);


/*
 * array_in :
 *		  将数组从外部格式 "字符串" 转换为
 *		  内部格式。
 *
 * 返回值 :
 *		  输入数组的内部表示
 */
Datum array_in(PG_FUNCTION_ARGS)
{
	char	   *fc_string = PG_GETARG_CSTRING(0);	/* 外部形式 */
	Oid			fc_element_type = PG_GETARG_OID(1);	/* 数组的类型
													 * 元素 */
	int32		fc_typmod = PG_GETARG_INT32(2);	/* 数组元素的 typmod */
	int			fc_typlen;
	bool		fc_typbyval;
	char		fc_typalign;
	char		fc_typdelim;
	Oid			fc_typioparam;
	char	   *fc_string_save,
			   *fc_p;
	int			fc_i,
				fc_nitems;
	Datum	   *fc_dataPtr;
	bool	   *fc_nullsPtr;
	bool		fc_hasnulls;
	int32		fc_nbytes;
	int32		fc_dataoffset;
	ArrayType  *fc_retval;
	int			fc_ndim,
				fc_dim[MAXDIM],
				fc_lBound[MAXDIM];
	ArrayMetaState *fc_my_extra;

	/*
	 * 我们安排一次查找元素类型的信息，包括其输入
	 * 转换过程，假设元素类型在我们下面不发生变化。
	 */
	fc_my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
	if (fc_my_extra == NULL)
	{
		fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
													  sizeof(ArrayMetaState));
		fc_my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
		fc_my_extra->element_type = ~fc_element_type;
	}

	if (fc_my_extra->element_type != fc_element_type)
	{
		/*
		 * 获取元素类型的信息，包括其输入转换过程
		 */
		get_type_io_data(fc_element_type, IOFunc_input,
						 &fc_my_extra->typlen, &fc_my_extra->typbyval,
						 &fc_my_extra->typalign, &fc_my_extra->typdelim,
						 &fc_my_extra->typioparam, &fc_my_extra->typiofunc);
		fmgr_info_cxt(fc_my_extra->typiofunc, &fc_my_extra->proc,
					  fcinfo->flinfo->fn_mcxt);
		fc_my_extra->element_type = fc_element_type;
	}
	fc_typlen = fc_my_extra->typlen;
	fc_typbyval = fc_my_extra->typbyval;
	fc_typalign = fc_my_extra->typalign;
	fc_typdelim = fc_my_extra->typdelim;
	fc_typioparam = fc_my_extra->typioparam;

	/* 制作输入的可修改副本 */
	fc_string_save = pstrdup(fc_string);

	/*
	 * 如果输入字符串以维度信息开头，则读取并使用它。
	 * 否则，我们要求输入采用大括号样式，我们
	 * 预先扫描输入以确定维度。
	 *
	 * 维度信息的形式为一个或多个 [n] 或 [m:n] 项。外部循环
	 * 每个维度项迭代一次。
	 */
	fc_p = fc_string_save;
	fc_ndim = 0;
	for (;;)
	{
		char	   *fc_q;
		int			fc_ub;

		/*
		 * 注意：我们当前允许在维度项之间有空格，但不允许
		 * 在维度项内部有空格。
		 */
		while (fc_array_isspace(*fc_p))
			fc_p++;
		if (*fc_p != '[')
			break;				/* 没有更多的维度项 */
		fc_p++;
		if (fc_ndim >= MAXDIM)
			ereport(ERROR,
					(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
					 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
							fc_ndim + 1, MAXDIM)));

		for (fc_q = fc_p; isdigit((unsigned char) *fc_q) || (*fc_q == '-') || (*fc_q == '+'); fc_q++)
			 /* 跳过 */ ;
		if (fc_q == fc_p)				/* 没有数字？ */
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
					 errmsg("malformed array literal: \"%s\"", fc_string),
					 errdetail("\"[\" must introduce explicitly-specified array dimensions.")));

		if (*fc_q == ':')
		{
			/* [m:n] 格式 */
			*fc_q = '\0';
			fc_lBound[fc_ndim] = atoi(fc_p);
			fc_p = fc_q + 1;
			for (fc_q = fc_p; isdigit((unsigned char) *fc_q) || (*fc_q == '-') || (*fc_q == '+'); fc_q++)
				 /* 跳过 */ ;
			if (fc_q == fc_p)			/* 没有数字？ */
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
						 errmsg("malformed array literal: \"%s\"", fc_string),
						 errdetail("Missing array dimension value.")));
		}
		else
		{
			/* [n] 格式 */
			fc_lBound[fc_ndim] = 1;
		}
		if (*fc_q != ']')
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
					 errmsg("malformed array literal: \"%s\"", fc_string),
					 errdetail("Missing \"%s\" after array dimensions.",
							   "]")));

		*fc_q = '\0';
		fc_ub = atoi(fc_p);
		fc_p = fc_q + 1;
		if (fc_ub < fc_lBound[fc_ndim])
			ereport(ERROR,
					(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
					 errmsg("upper bound cannot be less than lower bound")));

		fc_dim[fc_ndim] = fc_ub - fc_lBound[fc_ndim] + 1;
		fc_ndim++;
	}

	if (fc_ndim == 0)
	{
		/* 没有数组维度，因此从大括号结构推断维度 */
		if (*fc_p != '{')
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
					 errmsg("malformed array literal: \"%s\"", fc_string),
					 errdetail("Array value must start with \"{\" or dimension information.")));
		fc_ndim = fc_ArrayCount(fc_p, fc_dim, fc_typdelim);
		for (fc_i = 0; fc_i < fc_ndim; fc_i++)
			fc_lBound[fc_i] = 1;
	}
	else
	{
		int			fc_ndim_braces,
					fc_dim_braces[MAXDIM];

		/* 如果给出了数组维度，期望 '=' 操作符 */
		if (strncmp(fc_p, ASSGN, strlen(ASSGN)) != 0)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
					 errmsg("malformed array literal: \"%s\"", fc_string),
					 errdetail("Missing \"%s\" after array dimensions.",
							   ASSGN)));
		fc_p += strlen(ASSGN);
		while (fc_array_isspace(*fc_p))
			fc_p++;

		/*
		 * 从大括号结构推断维度 -- 它最好与我们
		 * 给出的匹配
		 */
		if (*fc_p != '{')
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
					 errmsg("malformed array literal: \"%s\"", fc_string),
					 errdetail("Array contents must start with \"{\".")));
		fc_ndim_braces = fc_ArrayCount(fc_p, fc_dim_braces, fc_typdelim);
		if (fc_ndim_braces != fc_ndim)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
					 errmsg("malformed array literal: \"%s\"", fc_string),
					 errdetail("Specified array dimensions do not match array contents.")));
		for (fc_i = 0; fc_i < fc_ndim; ++fc_i)
		{
			if (fc_dim[fc_i] != fc_dim_braces[fc_i])
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
						 errmsg("malformed array literal: \"%s\"", fc_string),
						 errdetail("Specified array dimensions do not match array contents.")));
		}
	}

#ifdef ARRAYDEBUG
	printf("array_in- ndim %d (", fc_ndim);
	for (fc_i = 0; fc_i < fc_ndim; fc_i++)
	{
		printf(" %d", fc_dim[fc_i]);
	};
	printf(") for %s\n", fc_string);
#endif

	/* 这检查数组维度的溢出 */
	fc_nitems = ArrayGetNItems(fc_ndim, fc_dim);
	ArrayCheckBounds(fc_ndim, fc_dim, fc_lBound);

	/* 空数组？ */
	if (fc_nitems == 0)
		PG_RETURN_ARRAYTYPE_P(construct_empty_array(fc_element_type));

	fc_dataPtr = (Datum *) palloc(fc_nitems * sizeof(Datum));
	fc_nullsPtr = (bool *) palloc(fc_nitems * sizeof(bool));
	fc_ReadArrayStr(fc_p, fc_string,
				 fc_nitems, fc_ndim, fc_dim,
				 &fc_my_extra->proc, fc_typioparam, fc_typmod,
				 fc_typdelim,
				 fc_typlen, fc_typbyval, fc_typalign,
				 fc_dataPtr, fc_nullsPtr,
				 &fc_hasnulls, &fc_nbytes);
	if (fc_hasnulls)
	{
		fc_dataoffset = ARR_OVERHEAD_WITHNULLS(fc_ndim, fc_nitems);
		fc_nbytes += fc_dataoffset;
	}
	else
	{
		fc_dataoffset = 0;			/* 表示没有空位图的标记 */
		fc_nbytes += ARR_OVERHEAD_NONULLS(fc_ndim);
	}
	fc_retval = (ArrayType *) palloc0(fc_nbytes);
	SET_VARSIZE(fc_retval, fc_nbytes);
	fc_retval->ndim = fc_ndim;
	fc_retval->dataoffset = fc_dataoffset;

	/* 
	 * 这来自数组的 pg_type.typelem（指向基
	 * 数据类型的 pg_type.oid）并在用户表中存储系统 oid。
	 * 此 oid 必须在二进制升级时保留。
	 */
	fc_retval->elemtype = fc_element_type;
	memcpy(ARR_DIMS(fc_retval), fc_dim, fc_ndim * sizeof(int));
	memcpy(ARR_LBOUND(fc_retval), fc_lBound, fc_ndim * sizeof(int));

	CopyArrayEls(fc_retval,
				 fc_dataPtr, fc_nullsPtr, fc_nitems,
				 fc_typlen, fc_typbyval, fc_typalign,
				 true);

	pfree(fc_dataPtr);
	pfree(fc_nullsPtr);
	pfree(fc_string_save);

	PG_RETURN_ARRAYTYPE_P(fc_retval);
}

/*
 * array_isspace() --- 一个与语言环境无关的 isspace()
 *
 * 我们以前使用 isspace() 来解析数组值，但这样会
 * 产生不良后果：数组值可能会根据语言环境设置
 * 被默默地解释为不同的结果。现在我们只硬编码
 * 传统的 ASCII isspace() 定义。
 */
static bool fc_array_isspace(char fc_ch)
{
	if (fc_ch == ' ' ||
		fc_ch == '\t' ||
		fc_ch == '\n' ||
		fc_ch == '\r' ||
		fc_ch == '\v' ||
		fc_ch == '\f')
		return true;
	return false;
}

/*
 * ArrayCount
 *	 确定数组字符串的维度。
 *
 * 返回维度数作为函数结果。轴的长度
 * 在 dim[] 中返回，dim[] 的大小必须为 MAXDIM。
 */
static int fc_ArrayCount(const char *fc_str, int *fc_dim, char fc_typdelim)
{
	int			fc_nest_level = 0,
				fc_i;
	int			fc_ndim = 1,
				fc_temp[MAXDIM],
				fc_nelems[MAXDIM],
				fc_nelems_last[MAXDIM];
	bool		fc_in_quotes = false;
	bool		fc_eoArray = false;
	bool		fc_empty_array = true;
	const char *fc_ptr;
	ArrayParseState fc_parse_state = ARRAY_NO_LEVEL;

	for (fc_i = 0; fc_i < MAXDIM; ++fc_i)
	{
		fc_temp[fc_i] = fc_dim[fc_i] = fc_nelems_last[fc_i] = 0;
		fc_nelems[fc_i] = 1;
	}

	fc_ptr = fc_str;
	while (!fc_eoArray)
	{
		bool		fc_itemdone = false;

		while (!fc_itemdone)
		{
			if (fc_parse_state == ARRAY_ELEM_STARTED ||
				fc_parse_state == ARRAY_QUOTED_ELEM_STARTED)
				fc_empty_array = false;

			switch (*fc_ptr)
			{
				case '\0':
					/* 信号字符串的提前结束 */
					ereport(ERROR,
							(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
							 errmsg("malformed array literal: \"%s\"", fc_str),
							 errdetail("Unexpected end of input.")));
					break;
				case '\\':

					/*
					 * 转义字符必须在层级开始、元素开始或元素分隔符之后。无论如何，我们现在必须经过一个元素开始。
					 */
					if (fc_parse_state != ARRAY_LEVEL_STARTED &&
						fc_parse_state != ARRAY_ELEM_STARTED &&
						fc_parse_state != ARRAY_QUOTED_ELEM_STARTED &&
						fc_parse_state != ARRAY_ELEM_DELIMITED)
						ereport(ERROR,
								(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
								 errmsg("malformed array literal: \"%s\"", fc_str),
								 errdetail("Unexpected \"%c\" character.",
										   '\\')));
					if (fc_parse_state != ARRAY_QUOTED_ELEM_STARTED)
						fc_parse_state = ARRAY_ELEM_STARTED;
					/* 跳过被转义的字符 */
					if (*(fc_ptr + 1))
						fc_ptr++;
					else
						ereport(ERROR,
								(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
								 errmsg("malformed array literal: \"%s\"", fc_str),
								 errdetail("Unexpected end of input.")));
					break;
				case '"':

					/*
					 * 引号必须在层级开始、引号元素开始或元素分隔符之后。无论如何，我们现在必须经过一个元素开始。
					 */
					if (fc_parse_state != ARRAY_LEVEL_STARTED &&
						fc_parse_state != ARRAY_QUOTED_ELEM_STARTED &&
						fc_parse_state != ARRAY_ELEM_DELIMITED)
						ereport(ERROR,
								(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
								 errmsg("malformed array literal: \"%s\"", fc_str),
								 errdetail("Unexpected array element.")));
					fc_in_quotes = !fc_in_quotes;
					if (fc_in_quotes)
						fc_parse_state = ARRAY_QUOTED_ELEM_STARTED;
					else
						fc_parse_state = ARRAY_QUOTED_ELEM_COMPLETED;
					break;
				case '{':
					if (!fc_in_quotes)
					{
						/*
						 * 左大括号可以发生在尚未发生嵌套的情况下、层级开始之后或层级分隔符之后。
						 */
						if (fc_parse_state != ARRAY_NO_LEVEL &&
							fc_parse_state != ARRAY_LEVEL_STARTED &&
							fc_parse_state != ARRAY_LEVEL_DELIMITED)
							ereport(ERROR,
									(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
									 errmsg("malformed array literal: \"%s\"", fc_str),
									 errdetail("Unexpected \"%c\" character.",
											   '{')));
						fc_parse_state = ARRAY_LEVEL_STARTED;
						if (fc_nest_level >= MAXDIM)
							ereport(ERROR,
									(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
									 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
											fc_nest_level + 1, MAXDIM)));
						fc_temp[fc_nest_level] = 0;
						fc_nest_level++;
						if (fc_ndim < fc_nest_level)
							fc_ndim = fc_nest_level;
					}
					break;
				case '}':
					if (!fc_in_quotes)
					{
						/*
						 * 右大括号可以在元素开始、元素完成、引号元素完成或层级完成之后发生。
						 */
						if (fc_parse_state != ARRAY_ELEM_STARTED &&
							fc_parse_state != ARRAY_ELEM_COMPLETED &&
							fc_parse_state != ARRAY_QUOTED_ELEM_COMPLETED &&
							fc_parse_state != ARRAY_LEVEL_COMPLETED &&
							!(fc_nest_level == 1 && fc_parse_state == ARRAY_LEVEL_STARTED))
							ereport(ERROR,
									(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
									 errmsg("malformed array literal: \"%s\"", fc_str),
									 errdetail("Unexpected \"%c\" character.",
											   '}')));
						fc_parse_state = ARRAY_LEVEL_COMPLETED;
						if (fc_nest_level == 0)
#ifdef FDD //cppcheck
						{
#endif
							ereport(ERROR,
									(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
									 errmsg("malformed array literal: \"%s\"", fc_str),
									 errdetail("Unmatched \"%c\" character.", '}')));
#ifdef FDD //cppcheck
							return 0;
						}
#endif
						fc_nest_level--;

						if (fc_nelems_last[fc_nest_level] != 0 &&
							fc_nelems[fc_nest_level] != fc_nelems_last[fc_nest_level])
#ifdef FDD //cppcheck
						{
#endif
							ereport(ERROR,
									(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
									 errmsg("malformed array literal: \"%s\"", fc_str),
									 errdetail("Multidimensional arrays must have "
											   "sub-arrays with matching "
											   "dimensions.")));
#ifdef FDD //cppcheck
							return 0;
						}
#endif
						fc_nelems_last[fc_nest_level] = fc_nelems[fc_nest_level];
						fc_nelems[fc_nest_level] = 1;
						if (fc_nest_level == 0)
							fc_eoArray = fc_itemdone = true;
						else
						{
							/*
							 * 我们在这里不设置itemdone；请参阅
							 * ReadArrayStr中的注释
							 */
							fc_temp[fc_nest_level - 1]++;
						}
					}
					break;
				default:
					if (!fc_in_quotes)
					{
						if (*fc_ptr == fc_typdelim)
						{
							/*
							 * 分隔符可以在元素开始、元素完成、引号元素完成或层级完成之后发生。
							 */
							if (fc_parse_state != ARRAY_ELEM_STARTED &&
								fc_parse_state != ARRAY_ELEM_COMPLETED &&
								fc_parse_state != ARRAY_QUOTED_ELEM_COMPLETED &&
								fc_parse_state != ARRAY_LEVEL_COMPLETED)
								ereport(ERROR,
										(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
										 errmsg("malformed array literal: \"%s\"", fc_str),
										 errdetail("Unexpected \"%c\" character.",
												   fc_typdelim)));
							if (fc_parse_state == ARRAY_LEVEL_COMPLETED)
								fc_parse_state = ARRAY_LEVEL_DELIMITED;
							else
								fc_parse_state = ARRAY_ELEM_DELIMITED;
							fc_itemdone = true;
							fc_nelems[fc_nest_level - 1]++;
						}
						else if (!fc_array_isspace(*fc_ptr))
						{
							/*
							 * 其他非空格字符必须在层级开始、元素开始或元素分隔符之后。无论如何，我们现在必须经过一个元素开始。
							 */
							if (fc_parse_state != ARRAY_LEVEL_STARTED &&
								fc_parse_state != ARRAY_ELEM_STARTED &&
								fc_parse_state != ARRAY_ELEM_DELIMITED)
								ereport(ERROR,
										(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
										 errmsg("malformed array literal: \"%s\"", fc_str),
										 errdetail("Unexpected array element.")));
							fc_parse_state = ARRAY_ELEM_STARTED;
						}
					}
					break;
			}
			if (!fc_itemdone)
				fc_ptr++;
		}
		fc_temp[fc_ndim - 1]++;
		fc_ptr++;
	}

	/* 只允许在闭合大括号后有空白字符 */
	while (*fc_ptr)
	{
		if (!fc_array_isspace(*fc_ptr++))
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
					 errmsg("malformed array literal: \"%s\"", fc_str),
					 errdetail("Junk after closing right brace.")));
	}

	/* 空数组的特例 */
	if (fc_empty_array)
		return 0;

	for (fc_i = 0; fc_i < fc_ndim; ++fc_i)
		fc_dim[fc_i] = fc_temp[fc_i];

	return fc_ndim;
}

/*
 * ReadArrayStr :
 *	 解析指向"arrayStr"的数组字符串并将值转换为内部格式。未指定的元素初始化为null。
 *	 数组的维度必须已经确定。
 *
 * 输入：
 *	arrayStr: 要解析的字符串。
 *			  注意："arrayStr"的内容将被修改！
 *	origStr: 未修改的输入字符串，仅在错误消息中使用。
 *	nitems: 已确定的数组元素总数。
 *	ndim: 数组维度的数量
 *	dim[]: 数组轴的长度
 *	inputproc: 针对元素数据类型的特定输入过程。
 *	typioparam, typmod: 传递给inputproc的辅助值。
 *	typdelim: 值分隔符（特定于类型）。
 *	typlen, typbyval, typalign: 元素数据类型的存储参数。
 *
 * 输出：
 *	values[]: 填充已转换的数据值。
 *	nulls[]: 填充是否为null的标记。
 *	*hasnulls: 如果存在任何null元素，则设置为true。
 *	*nbytes: 设置为所需数据区域的总大小（包括对齐填充，但不包括数组头的额外开销）。
 *
 * 请注意，values[]和nulls[]由调用者分配，并且必须具有nitems个元素。
 */
static void fc_ReadArrayStr(char *fc_arrayStr,
			 const char *fc_origStr,
			 int fc_nitems,
			 int fc_ndim,
			 int *fc_dim,
			 FmgrInfo *fc_inputproc,
			 Oid fc_typioparam,
			 int32 fc_typmod,
			 char fc_typdelim,
			 int fc_typlen,
			 bool fc_typbyval,
			 char fc_typalign,
			 Datum *fc_values,
			 bool *fc_nulls,
			 bool *fc_hasnulls,
			 int32 *fc_nbytes)
{
	int			fc_i,
				fc_nest_level = 0;
	char	   *fc_srcptr;
	bool		fc_in_quotes = false;
	bool		fc_eoArray = false;
	bool		fc_hasnull;
	int32		fc_totbytes;
	int			fc_indx[MAXDIM],
				fc_prod[MAXDIM];

	mda_get_prod(fc_ndim, fc_dim, fc_prod);
	MemSet(fc_indx, 0, sizeof(fc_indx));

	/* 将is-null标记初始化为true */
	memset(fc_nulls, true, fc_nitems * sizeof(bool));

	/*
	 * 我们必须移除"和\字符，以创建干净的项值以传递给数据类型输入例程。我们在arrayStr中就地覆盖每个项值。
	 * srcptr是当前扫描点，dstptr是我们正在复制的位置。
	 *
	 * 我们还想抑制前导和尾随的未引用空白。我们使用leadingspace标志来抑制前导空格。尾随空格通过使用dstendptr指向最后一个重要输出字符进行跟踪。
	 *
	 * 此例程中的错误检查主要是形式上的，因为我们预计ArrayCount()已经验证了字符串。因此，我们不必担心errdetail消息。
	 */
	fc_srcptr = fc_arrayStr;
	while (!fc_eoArray)
	{
		bool		fc_itemdone = false;
		bool		fc_leadingspace = true;
		bool		fc_hasquoting = false;
		char	   *fc_itemstart;
		char	   *fc_dstptr;
		char	   *fc_dstendptr;

		fc_i = -1;
		fc_itemstart = fc_dstptr = fc_dstendptr = fc_srcptr;

		while (!fc_itemdone)
		{
			switch (*fc_srcptr)
			{
				case '\0':
					/* 信号字符串的提前结束 */
					ereport(ERROR,
							(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
							 errmsg("malformed array literal: \"%s\"",
									fc_origStr)));
					break;
				case '\\':
					/* 跳过反斜杠，按原样复制下一个字符。 */
					fc_srcptr++;
					if (*fc_srcptr == '\0')
						ereport(ERROR,
								(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
								 errmsg("malformed array literal: \"%s\"",
										fc_origStr)));
					*fc_dstptr++ = *fc_srcptr++;
					/* 将被转义的字符视为非空白 */
					fc_leadingspace = false;
					fc_dstendptr = fc_dstptr;
					fc_hasquoting = true;	/* 不能是NULL标记 */
					break;
				case '"':
					fc_in_quotes = !fc_in_quotes;
					if (fc_in_quotes)
						fc_leadingspace = false;
					else
					{
						/*
						 * 退出in_quotes时，推进dstendptr；这可以节省在所有其他的in_quotes情况下执行此操作的需要。
						 */
						fc_dstendptr = fc_dstptr;
					}
					fc_hasquoting = true;	/* 不能是NULL标记 */
					fc_srcptr++;
					break;
				case '{':
					if (!fc_in_quotes)
					{
						if (fc_nest_level >= fc_ndim)
							ereport(ERROR,
									(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
									 errmsg("malformed array literal: \"%s\"",
											fc_origStr)));
						fc_nest_level++;
						fc_indx[fc_nest_level - 1] = 0;
						fc_srcptr++;
					}
					else
						*fc_dstptr++ = *fc_srcptr++;
					break;
				case '}':
					if (!fc_in_quotes)
					{
						if (fc_nest_level == 0)
							ereport(ERROR,
									(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
									 errmsg("malformed array literal: \"%s\"",
											fc_origStr)));
						if (fc_i == -1)
							fc_i = ArrayGetOffset0(fc_ndim, fc_indx, fc_prod);
						fc_indx[fc_nest_level - 1] = 0;
						fc_nest_level--;
						if (fc_nest_level == 0)
							fc_eoArray = fc_itemdone = true;
						else
							fc_indx[fc_nest_level - 1]++;
						fc_srcptr++;
					}
					else
						*fc_dstptr++ = *fc_srcptr++;
					break;
				default:
					if (fc_in_quotes)
						*fc_dstptr++ = *fc_srcptr++;
					else if (*fc_srcptr == fc_typdelim)
					{
						if (fc_i == -1)
							fc_i = ArrayGetOffset0(fc_ndim, fc_indx, fc_prod);
						fc_itemdone = true;
						fc_indx[fc_ndim - 1]++;
						fc_srcptr++;
					}
					else if (fc_array_isspace(*fc_srcptr))
					{
						/*
						 * 如果是前导空格，立即丢弃。如果不是，则复制但不推进dstendptr。
						 */
						if (fc_leadingspace)
							fc_srcptr++;
						else
							*fc_dstptr++ = *fc_srcptr++;
					}
					else
					{
						*fc_dstptr++ = *fc_srcptr++;
						fc_leadingspace = false;
						fc_dstendptr = fc_dstptr;
					}
					break;
			}
		}

		Assert(fc_dstptr < fc_srcptr);
		*fc_dstendptr = '\0';

		if (fc_i < 0 || fc_i >= fc_nitems)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
					 errmsg("malformed array literal: \"%s\"",
							fc_origStr)));

		if (Array_nulls && !fc_hasquoting &&
			pg_strcasecmp(fc_itemstart, "NULL") == 0)
		{
			/* 这是一个NULL项 */
			fc_values[fc_i] = InputFunctionCall(fc_inputproc, NULL,
										  fc_typioparam, fc_typmod);
			fc_nulls[fc_i] = true;
		}
		else
		{
			fc_values[fc_i] = InputFunctionCall(fc_inputproc, fc_itemstart,
										  fc_typioparam, fc_typmod);
			fc_nulls[fc_i] = false;
		}
	}

	/*
	 * 检查null，计算所需的总数据空间
	 */
	fc_hasnull = false;
	fc_totbytes = 0;
	for (fc_i = 0; fc_i < fc_nitems; fc_i++)
	{
		if (fc_nulls[fc_i])
			fc_hasnull = true;
		else
		{
			/* 让我们确保数据未被压缩 */
			if (fc_typlen == -1)
				fc_values[fc_i] = PointerGetDatum(PG_DETOAST_DATUM(fc_values[fc_i]));
			fc_totbytes = att_addlength_datum(fc_totbytes, fc_typlen, fc_values[fc_i]);
			fc_totbytes = att_align_nominal(fc_totbytes, fc_typalign);
			/* 检查总请求的溢出 */
			if (!AllocSizeIsValid(fc_totbytes))
				ereport(ERROR,
						(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
						 errmsg("array size exceeds the maximum allowed (%d)",
								(int) MaxAllocSize)));
		}
	}
	*fc_hasnulls = fc_hasnull;
	*fc_nbytes = fc_totbytes;
}


/*
 * 从暂时的Datums数组中复制数据到数组对象。
 *
 * array: 数组对象（其头部字段已经填充）
 * values: 要复制的Datums数组
 * nulls: is-null标志数组（如果没有null则可以为NULL）
 * nitems: 要复制的Datums数量
 * typbyval, typlen, typalign: 有关元素数据类型的信息
 * freedata: 如果为true且元素类型是按引用传递，则在复制后释放Datums引用的数据值。
 *
 * 如果输入数据是varlena类型，调用者必须确保值未被压缩。（在这里做不会有效，因为调用者已经为数组分配了空间……）
 */
void CopyArrayEls(ArrayType *fc_array,
			 Datum *fc_values,
			 bool *fc_nulls,
			 int fc_nitems,
			 int fc_typlen,
			 bool fc_typbyval,
			 char fc_typalign,
			 bool fc_freedata)
{
	char	   *fc_p = ARR_DATA_PTR(fc_array);
	bits8	   *fc_bitmap = ARR_NULLBITMAP(fc_array);
	int			fc_bitval = 0;
	int			fc_bitmask = 1;
	int			fc_i;

	if (fc_typbyval)
		fc_freedata = false;

	for (fc_i = 0; fc_i < fc_nitems; fc_i++)
	{
		if (fc_nulls && fc_nulls[fc_i])
		{
			if (!fc_bitmap)		/* 不应该发生 */
				elog(ERROR, "null array element where not supported");
			/* 位图位保持为0 */
		}
		else
		{
			fc_bitval |= fc_bitmask;
			fc_p += fc_ArrayCastAndSet(fc_values[fc_i], fc_typlen, fc_typbyval, fc_typalign, fc_p);
			if (fc_freedata)
				pfree(DatumGetPointer(fc_values[fc_i]));
		}
		if (fc_bitmap)
		{
			fc_bitmask <<= 1;
			if (fc_bitmask == 0x100)
			{
				*fc_bitmap++ = fc_bitval;
				fc_bitval = 0;
				fc_bitmask = 1;
			}
		}
	}

	if (fc_bitmap && fc_bitmask != 1)
		*fc_bitmap = fc_bitval;
}

/*
 * array_out :
 *		   接收数组的内部表示并返回包含其外部格式的字符串。
 */
Datum array_out(PG_FUNCTION_ARGS)
{
	AnyArrayType *fc_v = PG_GETARG_ANY_ARRAY_P(0);
	Oid			fc_element_type = AARR_ELEMTYPE(fc_v);
	int			fc_typlen;
	bool		fc_typbyval;
	char		fc_typalign;
	char		fc_typdelim;
	char	   *fc_p,
			   *fc_tmp,
			   *fc_retval,
			  **fc_values,
				fc_dims_str[(MAXDIM * 33) + 2];

	/*
	 * 每个维度 33，因为我们假设每个数字 15 位 + ':' +'[]'
	 *
	 * +2 允许赋值操作符 + 尾随空值
	 */
	bool	   *fc_needquotes,
				fc_needdims = false;
	size_t		fc_overall_length;
	int			fc_nitems,
				fc_i,
				fc_j,
				fc_k,
				fc_indx[MAXDIM];
	int			fc_ndim,
			   *fc_dims,
			   *fc_lb;
	array_iter	fc_iter;
	ArrayMetaState *fc_my_extra;

	/*
	 * 我们安排只在每一系列调用中查找元素类型的信息，包括其输出
	 * 转换过程，假设元素类型不会在我们之下发生变化。
	 */
	fc_my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
	if (fc_my_extra == NULL)
	{
		fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
													  sizeof(ArrayMetaState));
		fc_my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
		fc_my_extra->element_type = ~fc_element_type;
	}

	if (fc_my_extra->element_type != fc_element_type)
	{
		/*
		 * 获取元素类型的信息，包括其输出转换过程
		 */
		get_type_io_data(fc_element_type, IOFunc_output,
						 &fc_my_extra->typlen, &fc_my_extra->typbyval,
						 &fc_my_extra->typalign, &fc_my_extra->typdelim,
						 &fc_my_extra->typioparam, &fc_my_extra->typiofunc);
		fmgr_info_cxt(fc_my_extra->typiofunc, &fc_my_extra->proc,
					  fcinfo->flinfo->fn_mcxt);
		fc_my_extra->element_type = fc_element_type;
	}
	fc_typlen = fc_my_extra->typlen;
	fc_typbyval = fc_my_extra->typbyval;
	fc_typalign = fc_my_extra->typalign;
	fc_typdelim = fc_my_extra->typdelim;

	fc_ndim = AARR_NDIM(fc_v);
	fc_dims = AARR_DIMS(fc_v);
	fc_lb = AARR_LBOUND(fc_v);
	fc_nitems = ArrayGetNItems(fc_ndim, fc_dims);

	if (fc_nitems == 0)
	{
		fc_retval = pstrdup("{}");
		PG_RETURN_CSTRING(fc_retval);
	}

	/*
	 * 如果某个维度的下界不为1，我们将需要添加显式维度
	 */
	for (fc_i = 0; fc_i < fc_ndim; fc_i++)
	{
		if (fc_lb[fc_i] != 1)
		{
			fc_needdims = true;
			break;
		}
	}

	/*
	 * 将所有值转换为字符串形式，计算所需的总空间（包括
	 * 任何开销，如转义反斜杠），并检测每个项目是否需要双引号。
	 */
	fc_values = (char **) palloc(fc_nitems * sizeof(char *));
	fc_needquotes = (bool *) palloc(fc_nitems * sizeof(bool));
	fc_overall_length = 0;

	array_iter_setup(&fc_iter, fc_v);

	for (fc_i = 0; fc_i < fc_nitems; fc_i++)
	{
		Datum		fc_itemvalue;
		bool		fc_isnull;
		bool		fc_needquote;

		/* 获取源元素，检查是否为 NULL */
		fc_itemvalue = array_iter_next(&fc_iter, &fc_isnull, fc_i,
									fc_typlen, fc_typbyval, fc_typalign);

		if (fc_isnull)
		{
			fc_values[fc_i] = pstrdup("NULL");
			fc_overall_length += 4;
			fc_needquote = false;
		}
		else
		{
			fc_values[fc_i] = OutputFunctionCall(&fc_my_extra->proc, fc_itemvalue);

			/* 计算数据加上反斜杠；检测需要引号的字符 */
			if (fc_values[fc_i][0] == '\0')
				fc_needquote = true;	/* 强制对空字符串使用引号 */
			else if (pg_strcasecmp(fc_values[fc_i], "NULL") == 0)
				fc_needquote = true;	/* 强制对字面 NULL 使用引号 */
			else
				fc_needquote = false;

			for (fc_tmp = fc_values[fc_i]; *fc_tmp != '\0'; fc_tmp++)
			{
				char		fc_ch = *fc_tmp;

				fc_overall_length += 1;
				if (fc_ch == '"' || fc_ch == '\\')
				{
					fc_needquote = true;
					fc_overall_length += 1;
				}
				else if (fc_ch == '{' || fc_ch == '}' || fc_ch == fc_typdelim ||
						 fc_array_isspace(fc_ch))
					fc_needquote = true;
			}
		}

		fc_needquotes[fc_i] = fc_needquote;

		/* 如果需要，则计算双引号的对数 */
		if (fc_needquote)
			fc_overall_length += 2;
		/* 和逗号（或其他 typdelim 分隔符） */
		fc_overall_length += 1;
	}

	/* 
	 * 最后一个数组元素后面没有 typdelim 分隔符，
	 * 但这没关系；这个空格是为了尾随的 '\0'。
	 *
	 * 现在计算输出字符串中大括号对的总数。
	 */
	for (fc_i = fc_j = 0, fc_k = 1; fc_i < fc_ndim; fc_i++)
	{
		fc_j += fc_k, fc_k *= fc_dims[fc_i];
	}
	fc_overall_length += 2 * fc_j;

	/* 如果需要，格式化显式维度 */
	fc_dims_str[0] = '\0';
	if (fc_needdims)
	{
		char	   *fc_ptr = fc_dims_str;

		for (fc_i = 0; fc_i < fc_ndim; fc_i++)
		{
			sprintf(fc_ptr, "[%d:%d]", fc_lb[fc_i], fc_lb[fc_i] + fc_dims[fc_i] - 1);
			fc_ptr += strlen(fc_ptr);
		}
		*fc_ptr++ = *ASSGN;
		*fc_ptr = '\0';
		fc_overall_length += fc_ptr - fc_dims_str;
	}

	/* 现在构造输出字符串 */
	fc_retval = (char *) palloc(fc_overall_length);
	fc_p = fc_retval;

#define APPENDSTR(str)	(strcpy(fc_p, (str)), fc_p += strlen(fc_p))
#define APPENDCHAR(ch)	(*fc_p++ = (ch), *fc_p = '\0')

	if (fc_needdims)
		APPENDSTR(fc_dims_str);
	APPENDCHAR('{');
	for (fc_i = 0; fc_i < fc_ndim; fc_i++)
		fc_indx[fc_i] = 0;
	fc_j = 0;
	fc_k = 0;
	do
	{
		for (fc_i = fc_j; fc_i < fc_ndim - 1; fc_i++)
			APPENDCHAR('{');

		if (fc_needquotes[fc_k])
		{
			APPENDCHAR('"');
			for (fc_tmp = fc_values[fc_k]; *fc_tmp; fc_tmp++)
			{
				char		fc_ch = *fc_tmp;

				if (fc_ch == '"' || fc_ch == '\\')
					*fc_p++ = '\\';
				*fc_p++ = fc_ch;
			}
			*fc_p = '\0';
			APPENDCHAR('"');
		}
		else
			APPENDSTR(fc_values[fc_k]);
		pfree(fc_values[fc_k++]);

		for (fc_i = fc_ndim - 1; fc_i >= 0; fc_i--)
		{
			if (++(fc_indx[fc_i]) < fc_dims[fc_i])
			{
				APPENDCHAR(fc_typdelim);
				break;
			}
			else
			{
				fc_indx[fc_i] = 0;
				APPENDCHAR('}');
			}
		}
		fc_j = fc_i;
	} while (fc_j != -1);

#undef APPENDSTR
#undef APPENDCHAR

	/* 确保我们准确计算了字符串长度 */
	Assert(fc_overall_length == (fc_p - fc_retval + 1));

	pfree(fc_values);
	pfree(fc_needquotes);

	PG_RETURN_CSTRING(fc_retval);
}

/* 
 * array_recv :
 *		  将数组从外部二进制格式转换为
 *		  其内部格式。
 *
 * 返回值：
 *		  输入数组的内部表示 */
Datum array_recv(PG_FUNCTION_ARGS)
{
	StringInfo	fc_buf = (StringInfo) PG_GETARG_POINTER(0);
	Oid			fc_spec_element_type = PG_GETARG_OID(1);	/* 数组的类型
														 * 元素 */
	int32		fc_typmod = PG_GETARG_INT32(2);	/* 数组元素的 typmod */
	Oid			fc_element_type;
	int			fc_typlen;
	bool		fc_typbyval;
	char		fc_typalign;
	Oid			fc_typioparam;
	int			fc_i,
				fc_nitems;
	Datum	   *fc_dataPtr;
	bool	   *fc_nullsPtr;
	bool		fc_hasnulls;
	int32		fc_nbytes;
	int32		fc_dataoffset;
	ArrayType  *fc_retval;
	int			fc_ndim,
				fc_flags,
				fc_dim[MAXDIM],
				fc_lBound[MAXDIM];
	ArrayMetaState *fc_my_extra;

	/* 获取数组头信息 */
	fc_ndim = pq_getmsgint(fc_buf, 4);
	if (fc_ndim < 0)				/* 我们允许零维数组 */
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
				 errmsg("invalid number of dimensions: %d", fc_ndim)));
	if (fc_ndim > MAXDIM)
		ereport(ERROR,
				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
				 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
						fc_ndim, MAXDIM)));

	fc_flags = pq_getmsgint(fc_buf, 4);
	if (fc_flags != 0 && fc_flags != 1)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
				 errmsg("invalid array flags")));

	/* 检查数据中记录的元素类型 */
	fc_element_type = pq_getmsgint(fc_buf, sizeof(Oid));

	/* 
	 * 从安全的角度来看，输入的
	 * 元素类型是否与我们预期的匹配并不重要：元素类型的接收
	 * 函数必须足够稳健以应对无效数据。 然而，
	 * 从用户友好的角度来看，抱怨类型
	 * 不匹配比抛出“非法二进制格式”错误要好。 但是有一个
	 * 问题：只有内置类型的 OID 稳定得足够，
	 * 以至于相信不匹配是一个真正的问题。 所以只有当两个 OID
	 * 都在内置范围内时才抱怨。 否则，继续使用我们“应该”
	 * 获取的元素类型。
	 */
	if (fc_element_type != fc_spec_element_type)
	{
		if (fc_element_type < FirstGenbkiObjectId &&
			fc_spec_element_type < FirstGenbkiObjectId)
			ereport(ERROR,
					(errcode(ERRCODE_DATATYPE_MISMATCH),
					 errmsg("binary data has array element type %u (%s) instead of expected %u (%s)",
							fc_element_type,
							format_type_extended(fc_element_type, -1,
												 FORMAT_TYPE_ALLOW_INVALID),
							fc_spec_element_type,
							format_type_extended(fc_spec_element_type, -1,
												 FORMAT_TYPE_ALLOW_INVALID))));
		fc_element_type = fc_spec_element_type;
	}

	for (fc_i = 0; fc_i < fc_ndim; fc_i++)
	{
		fc_dim[fc_i] = pq_getmsgint(fc_buf, 4);
		fc_lBound[fc_i] = pq_getmsgint(fc_buf, 4);
	}

	/* 检查数组维度是否溢出 */
	fc_nitems = ArrayGetNItems(fc_ndim, fc_dim);
	ArrayCheckBounds(fc_ndim, fc_dim, fc_lBound);

	/* 
	 * 我们安排只需在一系列调用中查找有关元素类型的信息，
	 * 包括其接收转换过程，假设元素
	 * 类型在我们面前不会发生变化。
	 */
	fc_my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
	if (fc_my_extra == NULL)
	{
		fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
													  sizeof(ArrayMetaState));
		fc_my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
		fc_my_extra->element_type = ~fc_element_type;
	}

	if (fc_my_extra->element_type != fc_element_type)
	{
		/* 获取有关元素类型的信息，包括其接收过程 */
		get_type_io_data(fc_element_type, IOFunc_receive,
						 &fc_my_extra->typlen, &fc_my_extra->typbyval,
						 &fc_my_extra->typalign, &fc_my_extra->typdelim,
						 &fc_my_extra->typioparam, &fc_my_extra->typiofunc);
		if (!OidIsValid(fc_my_extra->typiofunc))
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_FUNCTION),
					 errmsg("no binary input function available for type %s",
							format_type_be(fc_element_type))));
		fmgr_info_cxt(fc_my_extra->typiofunc, &fc_my_extra->proc,
					  fcinfo->flinfo->fn_mcxt);
		fc_my_extra->element_type = fc_element_type;
	}

	if (fc_nitems == 0)
	{
		/* 返回空数组... 但在验证 element_type 之前不返回 */
		PG_RETURN_ARRAYTYPE_P(construct_empty_array(fc_element_type));
	}

	fc_typlen = fc_my_extra->typlen;
	fc_typbyval = fc_my_extra->typbyval;
	fc_typalign = fc_my_extra->typalign;
	fc_typioparam = fc_my_extra->typioparam;

	fc_dataPtr = (Datum *) palloc(fc_nitems * sizeof(Datum));
	fc_nullsPtr = (bool *) palloc(fc_nitems * sizeof(bool));
	fc_ReadArrayBinary(fc_buf, fc_nitems,
					&fc_my_extra->proc, fc_typioparam, fc_typmod,
					fc_typlen, fc_typbyval, fc_typalign,
					fc_dataPtr, fc_nullsPtr,
					&fc_hasnulls, &fc_nbytes);
	if (fc_hasnulls)
	{
		fc_dataoffset = ARR_OVERHEAD_WITHNULLS(fc_ndim, fc_nitems);
		fc_nbytes += fc_dataoffset;
	}
	else
	{
		fc_dataoffset = 0;			/* 表示没有空位图的标记 */
		fc_nbytes += ARR_OVERHEAD_NONULLS(fc_ndim);
	}
	fc_retval = (ArrayType *) palloc0(fc_nbytes);
	SET_VARSIZE(fc_retval, fc_nbytes);
	fc_retval->ndim = fc_ndim;
	fc_retval->dataoffset = fc_dataoffset;
	fc_retval->elemtype = fc_element_type;
	memcpy(ARR_DIMS(fc_retval), fc_dim, fc_ndim * sizeof(int));
	memcpy(ARR_LBOUND(fc_retval), fc_lBound, fc_ndim * sizeof(int));

	CopyArrayEls(fc_retval,
				 fc_dataPtr, fc_nullsPtr, fc_nitems,
				 fc_typlen, fc_typbyval, fc_typalign,
				 true);

	pfree(fc_dataPtr);
	pfree(fc_nullsPtr);

	PG_RETURN_ARRAYTYPE_P(fc_retval);
}

/* 
 * ReadArrayBinary:
 *	 收集作为二进制样式读取的数组的数据元素。
 *
 * 输入：
 *	buf：要读取的数据缓冲区。
 *	nitems：数组元素的总数（已经读取）。
 *	receiveproc：特定于类型的接收过程，用于元素数据类型。
 *	typioparam，typmod：要传递给 receiveproc 的辅助值。
 *	typlen，typbyval，typalign：元素数据类型的存储参数。
 *
 * 输出：
 *	values[]：填充转换后的数据值。
 *	nulls[]：填充空值标记。
 *	*hasnulls：如果有任何空元素，设置为真。
 *	*nbytes：设置为所需数据区域的总大小（包括对齐
 *	 填充，但不包括数组头开销）。
 *
 * 注意，values[] 和 nulls[] 由调用者分配，并且必须具有
 * nitems 元素。
 */
static void fc_ReadArrayBinary(StringInfo fc_buf,
				int fc_nitems,
				FmgrInfo *fc_receiveproc,
				Oid fc_typioparam,
				int32 fc_typmod,
				int fc_typlen,
				bool fc_typbyval,
				char fc_typalign,
				Datum *fc_values,
				bool *fc_nulls,
				bool *fc_hasnulls,
				int32 *fc_nbytes)
{
	int			fc_i;
	bool		fc_hasnull;
	int32		fc_totbytes;

	for (fc_i = 0; fc_i < fc_nitems; fc_i++)
	{
		int			fc_itemlen;
		StringInfoData fc_elem_buf;
		char		fc_csave;

		/* 获取并检查项长度 */
		fc_itemlen = pq_getmsgint(fc_buf, 4);
		if (fc_itemlen < -1 || fc_itemlen > (fc_buf->len - fc_buf->cursor))
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
					 errmsg("insufficient data left in message")));

		if (fc_itemlen == -1)
		{
			/* -1 长度表示 NULL */
			fc_values[fc_i] = ReceiveFunctionCall(fc_receiveproc, NULL,
											fc_typioparam, fc_typmod);
			fc_nulls[fc_i] = true;
			continue;
		}

		/* 
		 * 与其复制数据，不如设置一个虚假的 StringInfo
		 * 指向输入缓冲区的正确部分。我们假设我们
		 * 可以对输入缓冲区进行处理，以维持
		 * StringInfos 具有尾随空值的约定。
		 */
		fc_elem_buf.data = &fc_buf->data[fc_buf->cursor];
		fc_elem_buf.maxlen = fc_itemlen + 1;
		fc_elem_buf.len = fc_itemlen;
		fc_elem_buf.cursor = 0;

		fc_buf->cursor += fc_itemlen;

		fc_csave = fc_buf->data[fc_buf->cursor];
		fc_buf->data[fc_buf->cursor] = '\0';

		/* 现在调用元素的 receiveproc */
		fc_values[fc_i] = ReceiveFunctionCall(fc_receiveproc, &fc_elem_buf,
										fc_typioparam, fc_typmod);
		fc_nulls[fc_i] = false;

		/* 如果没有读取完整个缓冲区则会出问题 */
		if (fc_elem_buf.cursor != fc_itemlen)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
					 errmsg("improper binary format in array element %d",
							fc_i + 1)));

		fc_buf->data[fc_buf->cursor] = fc_csave;
	}

	/*
	 * 检查null，计算所需的总数据空间
	 */
	fc_hasnull = false;
	fc_totbytes = 0;
	for (fc_i = 0; fc_i < fc_nitems; fc_i++)
	{
		if (fc_nulls[fc_i])
			fc_hasnull = true;
		else
		{
			/* 让我们确保数据未被压缩 */
			if (fc_typlen == -1)
				fc_values[fc_i] = PointerGetDatum(PG_DETOAST_DATUM(fc_values[fc_i]));
			fc_totbytes = att_addlength_datum(fc_totbytes, fc_typlen, fc_values[fc_i]);
			fc_totbytes = att_align_nominal(fc_totbytes, fc_typalign);
			/* 检查总请求的溢出 */
			if (!AllocSizeIsValid(fc_totbytes))
				ereport(ERROR,
						(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
						 errmsg("array size exceeds the maximum allowed (%d)",
								(int) MaxAllocSize)));
		}
	}
	*fc_hasnulls = fc_hasnull;
	*fc_nbytes = fc_totbytes;
}


/* 
 * array_send :
 *		  获取数组的内部表示并返回一个 bytea
 *		  包含外部二进制格式的数组。
 */
Datum array_send(PG_FUNCTION_ARGS)
{
	AnyArrayType *fc_v = PG_GETARG_ANY_ARRAY_P(0);
	Oid			fc_element_type = AARR_ELEMTYPE(fc_v);
	int			fc_typlen;
	bool		fc_typbyval;
	char		fc_typalign;
	int			fc_nitems,
				fc_i;
	int			fc_ndim,
			   *fc_dim,
			   *fc_lb;
	StringInfoData fc_buf;
	array_iter	fc_iter;
	ArrayMetaState *fc_my_extra;

	/* 
	 * 我们安排只需在一系列调用中查找有关元素类型的信息，
	 * 包括其发送转换过程，假设元素
	 * 类型在我们面前不会发生变化。
	 */
	fc_my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
	if (fc_my_extra == NULL)
	{
		fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
													  sizeof(ArrayMetaState));
		fc_my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
		fc_my_extra->element_type = ~fc_element_type;
	}

	if (fc_my_extra->element_type != fc_element_type)
	{
		/* 获取有关元素类型的信息，包括其发送过程 */
		get_type_io_data(fc_element_type, IOFunc_send,
						 &fc_my_extra->typlen, &fc_my_extra->typbyval,
						 &fc_my_extra->typalign, &fc_my_extra->typdelim,
						 &fc_my_extra->typioparam, &fc_my_extra->typiofunc);
		if (!OidIsValid(fc_my_extra->typiofunc))
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_FUNCTION),
					 errmsg("no binary output function available for type %s",
							format_type_be(fc_element_type))));
		fmgr_info_cxt(fc_my_extra->typiofunc, &fc_my_extra->proc,
					  fcinfo->flinfo->fn_mcxt);
		fc_my_extra->element_type = fc_element_type;
	}
	fc_typlen = fc_my_extra->typlen;
	fc_typbyval = fc_my_extra->typbyval;
	fc_typalign = fc_my_extra->typalign;

	fc_ndim = AARR_NDIM(fc_v);
	fc_dim = AARR_DIMS(fc_v);
	fc_lb = AARR_LBOUND(fc_v);
	fc_nitems = ArrayGetNItems(fc_ndim, fc_dim);

	pq_begintypsend(&fc_buf);

	/* 发送数组头信息 */
	pq_sendint32(&fc_buf, fc_ndim);
	pq_sendint32(&fc_buf, AARR_HASNULL(fc_v) ? 1 : 0);
	pq_sendint32(&fc_buf, fc_element_type);
	for (fc_i = 0; fc_i < fc_ndim; fc_i++)
	{
		pq_sendint32(&fc_buf, fc_dim[fc_i]);
		pq_sendint32(&fc_buf, fc_lb[fc_i]);
	}

	/* 使用元素自己的 sendproc 发送数组元素 */
	array_iter_setup(&fc_iter, fc_v);

	for (fc_i = 0; fc_i < fc_nitems; fc_i++)
	{
		Datum		fc_itemvalue;
		bool		fc_isnull;

		/* 获取源元素，检查是否为 NULL */
		fc_itemvalue = array_iter_next(&fc_iter, &fc_isnull, fc_i,
									fc_typlen, fc_typbyval, fc_typalign);

		if (fc_isnull)
		{
			/* -1 长度表示 NULL */
			pq_sendint32(&fc_buf, -1);
		}
		else
		{
			bytea	   *fc_outputbytes;

			fc_outputbytes = SendFunctionCall(&fc_my_extra->proc, fc_itemvalue);
			pq_sendint32(&fc_buf, VARSIZE(fc_outputbytes) - VARHDRSZ);
			pq_sendbytes(&fc_buf, VARDATA(fc_outputbytes),
						 VARSIZE(fc_outputbytes) - VARHDRSZ);
			pfree(fc_outputbytes);
		}
	}

	PG_RETURN_BYTEA_P(pq_endtypsend(&fc_buf));
}

/* 
 * array_ndims :
 *		  返回指向“v”的数组的维数
 */
Datum array_ndims(PG_FUNCTION_ARGS)
{
	AnyArrayType *fc_v = PG_GETARG_ANY_ARRAY_P(0);

	/* 精度检查：看起来真的是一个数组吗？ */
	if (AARR_NDIM(fc_v) <= 0 || AARR_NDIM(fc_v) > MAXDIM)
		PG_RETURN_NULL();

	PG_RETURN_INT32(AARR_NDIM(fc_v));
}

/*
 * array_dims :
 *		  返回指向 "v" 的数组的维度，以 "text" 形式返回
 */
Datum array_dims(PG_FUNCTION_ARGS)
{
	AnyArrayType *fc_v = PG_GETARG_ANY_ARRAY_P(0);
	char	   *fc_p;
	int			fc_i;
	int		   *fc_dimv,
			   *fc_lb;

	/*
	 * 33，因为我们假设每个数字有 15 个数字 + ':' +'[]'
	 *
	 * +1 用于结尾的空值
	 */
	char		fc_buf[MAXDIM * 33 + 1];

	/* 精度检查：看起来真的是一个数组吗？ */
	if (AARR_NDIM(fc_v) <= 0 || AARR_NDIM(fc_v) > MAXDIM)
		PG_RETURN_NULL();

	fc_dimv = AARR_DIMS(fc_v);
	fc_lb = AARR_LBOUND(fc_v);

	fc_p = fc_buf;
	for (fc_i = 0; fc_i < AARR_NDIM(fc_v); fc_i++)
	{
		sprintf(fc_p, "[%d:%d]", fc_lb[fc_i], fc_dimv[fc_i] + fc_lb[fc_i] - 1);
		fc_p += strlen(fc_p);
	}

	PG_RETURN_TEXT_P(cstring_to_text(fc_buf));
}

/*
 * array_lower :
 *		返回指向 "v" 的数组中请求的维度的下限，以 int4 形式返回
 */
Datum array_lower(PG_FUNCTION_ARGS)
{
	AnyArrayType *fc_v = PG_GETARG_ANY_ARRAY_P(0);
	int			fc_reqdim = PG_GETARG_INT32(1);
	int		   *fc_lb;
	int			fc_result;

	/* 精度检查：看起来真的是一个数组吗？ */
	if (AARR_NDIM(fc_v) <= 0 || AARR_NDIM(fc_v) > MAXDIM)
		PG_RETURN_NULL();

	/* 合理性检查：请求的维度是否有效 */
	if (fc_reqdim <= 0 || fc_reqdim > AARR_NDIM(fc_v))
		PG_RETURN_NULL();

	fc_lb = AARR_LBOUND(fc_v);
	fc_result = fc_lb[fc_reqdim - 1];

	PG_RETURN_INT32(fc_result);
}

/*
 * array_upper :
 *		返回指向 "v" 的数组中请求的维度的上限，以 int4 形式返回
 */
Datum array_upper(PG_FUNCTION_ARGS)
{
	AnyArrayType *fc_v = PG_GETARG_ANY_ARRAY_P(0);
	int			fc_reqdim = PG_GETARG_INT32(1);
	int		   *fc_dimv,
			   *fc_lb;
	int			fc_result;

	/* 精度检查：看起来真的是一个数组吗？ */
	if (AARR_NDIM(fc_v) <= 0 || AARR_NDIM(fc_v) > MAXDIM)
		PG_RETURN_NULL();

	/* 合理性检查：请求的维度是否有效 */
	if (fc_reqdim <= 0 || fc_reqdim > AARR_NDIM(fc_v))
		PG_RETURN_NULL();

	fc_lb = AARR_LBOUND(fc_v);
	fc_dimv = AARR_DIMS(fc_v);

	fc_result = fc_dimv[fc_reqdim - 1] + fc_lb[fc_reqdim - 1] - 1;

	PG_RETURN_INT32(fc_result);
}

/*
 * array_length :
 *		返回指向 "v" 的数组中请求的维度的长度，以 int4 形式返回
 */
Datum array_length(PG_FUNCTION_ARGS)
{
	AnyArrayType *fc_v = PG_GETARG_ANY_ARRAY_P(0);
	int			fc_reqdim = PG_GETARG_INT32(1);
	int		   *fc_dimv;
	int			fc_result;

	/* 精度检查：看起来真的是一个数组吗？ */
	if (AARR_NDIM(fc_v) <= 0 || AARR_NDIM(fc_v) > MAXDIM)
		PG_RETURN_NULL();

	/* 合理性检查：请求的维度是否有效 */
	if (fc_reqdim <= 0 || fc_reqdim > AARR_NDIM(fc_v))
		PG_RETURN_NULL();

	fc_dimv = AARR_DIMS(fc_v);

	fc_result = fc_dimv[fc_reqdim - 1];

	PG_RETURN_INT32(fc_result);
}

/*
 * array_cardinality:
 *		返回数组中的元素总数
 */
Datum array_cardinality(PG_FUNCTION_ARGS)
{
	AnyArrayType *fc_v = PG_GETARG_ANY_ARRAY_P(0);

	PG_RETURN_INT32(ArrayGetNItems(AARR_NDIM(fc_v), AARR_DIMS(fc_v)));
}


/*
 * array_get_element :
 *	  此例程接收一个数组数据和一个下标数组，并返回
 *	  被引用的项目作为 Datum。请注意，对于按引用传递的
 *	  数据类型，返回的 Datum 是指向数组对象的指针。
 *
 * 这处理普通的 varlena 数组和固定长度数组。
 *
 * 输入：
 *	arraydatum: 数组对象（不能为空）
 *	nSubscripts: 提供的下标数量
 *	indx[]: 下标值
 *	arraytyplen: 数组类型的 pg_type.typlen
 *	elmlen: 数组元素类型的 pg_type.typlen
 *	elmbyval: 数组元素类型的 pg_type.typbyval
 *	elmalign: 数组元素类型的 pg_type.typalign
 *
 * 输出：
 *	返回值是元素 Datum。
 *	*isNull 设置为指示元素是否为 NULL。
 */
Datum array_get_element(Datum fc_arraydatum,
				  int fc_nSubscripts,
				  int *fc_indx,
				  int fc_arraytyplen,
				  int fc_elmlen,
				  bool fc_elmbyval,
				  char fc_elmalign,
				  bool *fc_isNull)
{
	int			fc_i,
				fc_ndim,
			   *fc_dim,
			   *fc_lb,
				fc_offset,
				fc_fixedDim[1],
				fc_fixedLb[1];
	char	   *fc_arraydataptr,
			   *fc_retptr;
	bits8	   *fc_arraynullsptr;

	if (fc_arraytyplen > 0)
	{
		/*
		 * 固定长度数组 -- 假定为 1-d，基于 0
		 */
		fc_ndim = 1;
		fc_fixedDim[0] = fc_arraytyplen / fc_elmlen;
		fc_fixedLb[0] = 0;
		fc_dim = fc_fixedDim;
		fc_lb = fc_fixedLb;
		fc_arraydataptr = (char *) DatumGetPointer(fc_arraydatum);
		fc_arraynullsptr = NULL;
	}
	else if (VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(fc_arraydatum)))
	{
		/* 扩展数组：让我们在一个单独的函数中完成此操作 */
		return fc_array_get_element_expanded(fc_arraydatum,
										  fc_nSubscripts,
										  fc_indx,
										  fc_arraytyplen,
										  fc_elmlen,
										  fc_elmbyval,
										  fc_elmalign,
										  fc_isNull);
	}
	else
	{
		/* 如有必要去除数组的 Toast，生成正常的 varlena 输入 */
		ArrayType  *fc_array = DatumGetArrayTypeP(fc_arraydatum);

		fc_ndim = ARR_NDIM(fc_array);
		fc_dim = ARR_DIMS(fc_array);
		fc_lb = ARR_LBOUND(fc_array);
		fc_arraydataptr = ARR_DATA_PTR(fc_array);
		fc_arraynullsptr = ARR_NULLBITMAP(fc_array);
	}

	/*
	 * 针对无效下标返回 NULL
	 */
	if (fc_ndim != fc_nSubscripts || fc_ndim <= 0 || fc_ndim > MAXDIM)
	{
		*fc_isNull = true;
		return (Datum) 0;
	}
	for (fc_i = 0; fc_i < fc_ndim; fc_i++)
	{
		if (fc_indx[fc_i] < fc_lb[fc_i] || fc_indx[fc_i] >= (fc_dim[fc_i] + fc_lb[fc_i]))
		{
			*fc_isNull = true;
			return (Datum) 0;
		}
	}

	/*
	 * 计算元素编号
	 */
	fc_offset = ArrayGetOffset(fc_nSubscripts, fc_dim, fc_lb, fc_indx);

	/*
	 * 检查 NULL 数组元素
	 */
	if (fc_array_get_isnull(fc_arraynullsptr, fc_offset))
	{
		*fc_isNull = true;
		return (Datum) 0;
	}

	/*
	 * 好的，获取元素
	 */
	*fc_isNull = false;
	fc_retptr = fc_array_seek(fc_arraydataptr, 0, fc_arraynullsptr, fc_offset,
						fc_elmlen, fc_elmbyval, fc_elmalign);
	return fc_ArrayCast(fc_retptr, fc_elmbyval, fc_elmlen);
}

/*
 * array_get_element() 的实现，适用于扩展数组
 */
static Datum fc_array_get_element_expanded(Datum fc_arraydatum,
						   int fc_nSubscripts, int *fc_indx,
						   int fc_arraytyplen,
						   int fc_elmlen, bool fc_elmbyval, char fc_elmalign,
						   bool *fc_isNull)
{
	ExpandedArrayHeader *fc_eah;
	int			fc_i,
				fc_ndim,
			   *fc_dim,
			   *fc_lb,
				fc_offset;
	Datum	   *fc_dvalues;
	bool	   *fc_dnulls;

	fc_eah = (ExpandedArrayHeader *) DatumGetEOHP(fc_arraydatum);
	Assert(fc_eah->ea_magic == EA_MAGIC);

	/* 对调用者的信息进行合理性检查，检查对象 */
	Assert(fc_arraytyplen == -1);
	Assert(fc_elmlen == fc_eah->typlen);
	Assert(fc_elmbyval == fc_eah->typbyval);
	Assert(fc_elmalign == fc_eah->typalign);

	fc_ndim = fc_eah->ndims;
	fc_dim = fc_eah->dims;
	fc_lb = fc_eah->lbound;

	/*
	 * 针对无效下标返回 NULL
	 */
	if (fc_ndim != fc_nSubscripts || fc_ndim <= 0 || fc_ndim > MAXDIM)
	{
		*fc_isNull = true;
		return (Datum) 0;
	}
	for (fc_i = 0; fc_i < fc_ndim; fc_i++)
	{
		if (fc_indx[fc_i] < fc_lb[fc_i] || fc_indx[fc_i] >= (fc_dim[fc_i] + fc_lb[fc_i]))
		{
			*fc_isNull = true;
			return (Datum) 0;
		}
	}

	/*
	 * 计算元素编号
	 */
	fc_offset = ArrayGetOffset(fc_nSubscripts, fc_dim, fc_lb, fc_indx);

	/*
	 * 如果我们还没有解构数组，则解构数组。请注意，即使输入
	 * 名义上是只读的，我们也会应用此操作：这应该是足够安全的。
	 */
	deconstruct_expanded_array(fc_eah);

	fc_dvalues = fc_eah->dvalues;
	fc_dnulls = fc_eah->dnulls;

	/*
	 * 检查 NULL 数组元素
	 */
	if (fc_dnulls && fc_dnulls[fc_offset])
	{
		*fc_isNull = true;
		return (Datum) 0;
	}

	/*
	 * 好的，获取元素。由于标准的 array_get_element 可以返回平面数组的指针
	 * ，因此返回一个通过引用传递的值作为指向扩展数组的指针是可以的：
	 * 假设值在 Datum 引用存在的期间不会改变。
	 */
	*fc_isNull = false;
	return fc_dvalues[fc_offset];
}

/*
 * array_get_slice :
 *		   此例程接受一个数组和一系列索引（upperIndx 和
 *		   lowerIndx），为引用的元素创建一个新的数组结构
 *		   并返回指向它的指针。
 *
 * 此功能处理普通的 varlena 数组和固定长度数组。
 *
 * 输入：
 *	arraydatum: 数组对象（不能为 NULL）
 *	nSubscripts: 提供的下标数（upper/lower 必须相同）
 *	upperIndx[]: 上下标值
 *	lowerIndx[]: 下标值
 *	upperProvided[]: true 表示提供了上标值
 *	lowerProvided[]: true 表示提供了下标值
 *	arraytyplen: 数组类型的 pg_type.typlen
 *	elmlen: 数组元素类型的 pg_type.typlen
 *	elmbyval: 数组元素类型的 pg_type.typbyval
 *	elmalign: 数组元素类型的 pg_type.typalign
 *
 * 输出：
 *	返回值是新的数组 Datum（绝不会为 NULL）
 *
 * 省略的上标和下标值将被对应的数组边界替换。
 *
 * 注意：我们假设可以在提供的下标数组 lowerIndx[] 和 upperIndx[]
 * 上进行操作；此外，即使 nSubscripts 较少，这些数组的大小也必须为 MAXDIM。 
 * 这些通常只是临时数组。
 */
Datum array_get_slice(Datum fc_arraydatum,
				int fc_nSubscripts,
				int *fc_upperIndx,
				int *fc_lowerIndx,
				bool *fc_upperProvided,
				bool *fc_lowerProvided,
				int fc_arraytyplen,
				int fc_elmlen,
				bool fc_elmbyval,
				char fc_elmalign)
{
	ArrayType  *fc_array;
	ArrayType  *fc_newarray;
	int			fc_i,
				fc_ndim,
			   *fc_dim,
			   *fc_lb,
			   *fc_newlb;
	int			fc_fixedDim[1],
				fc_fixedLb[1];
	Oid			fc_elemtype;
	char	   *fc_arraydataptr;
	bits8	   *fc_arraynullsptr;
	int32		fc_dataoffset;
	int			fc_bytes,
				fc_span[MAXDIM];

	if (fc_arraytyplen > 0)
	{
		/*
		 * 固定长度数组 -- 当前，无法对其进行切片，因为解析器
		 * 将输出标记为固定长度数组类型！下面的代码显示了
		 * 如果分析器被更改以将输出标记为合适的 varlena 数组类型，我们可以如何支持它。
		 */
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("slices of fixed-length arrays not implemented")));

		/*
		 * 固定长度数组 -- 假定为一维，基于 0
		 *
		 * XXX 我们将从哪里获得正确的 ELEMTYPE？
		 */
		fc_ndim = 1;
		fc_fixedDim[0] = fc_arraytyplen / fc_elmlen;
		fc_fixedLb[0] = 0;
		fc_dim = fc_fixedDim;
		fc_lb = fc_fixedLb;
		fc_elemtype = InvalidOid;	/* XXX */
		fc_arraydataptr = (char *) DatumGetPointer(fc_arraydatum);
		fc_arraynullsptr = NULL;
	}
	else
	{
		/* 如有必要，请解压输入数组 */
		fc_array = DatumGetArrayTypeP(fc_arraydatum);

		fc_ndim = ARR_NDIM(fc_array);
		fc_dim = ARR_DIMS(fc_array);
		fc_lb = ARR_LBOUND(fc_array);
		fc_elemtype = ARR_ELEMTYPE(fc_array);
		fc_arraydataptr = ARR_DATA_PTR(fc_array);
		fc_arraynullsptr = ARR_NULLBITMAP(fc_array);
	}

	/*
	 * 检查提供的下标。超过当前数组限制的切片将被静默截断为数组限制。
	 * 如果我们最终得到一个空切片，则返回一个空数组。
	 */
	if (fc_ndim < fc_nSubscripts || fc_ndim <= 0 || fc_ndim > MAXDIM)
		return PointerGetDatum(construct_empty_array(fc_elemtype));

	for (fc_i = 0; fc_i < fc_nSubscripts; fc_i++)
	{
		if (!fc_lowerProvided[fc_i] || fc_lowerIndx[fc_i] < fc_lb[fc_i])
			fc_lowerIndx[fc_i] = fc_lb[fc_i];
		if (!fc_upperProvided[fc_i] || fc_upperIndx[fc_i] >= (fc_dim[fc_i] + fc_lb[fc_i]))
			fc_upperIndx[fc_i] = fc_dim[fc_i] + fc_lb[fc_i] - 1;
		if (fc_lowerIndx[fc_i] > fc_upperIndx[fc_i])
			return PointerGetDatum(construct_empty_array(fc_elemtype));
	}
	/* 用完整数组范围填充任何缺失的下标位置 */
	for (; fc_i < fc_ndim; fc_i++)
	{
		fc_lowerIndx[fc_i] = fc_lb[fc_i];
		fc_upperIndx[fc_i] = fc_dim[fc_i] + fc_lb[fc_i] - 1;
		if (fc_lowerIndx[fc_i] > fc_upperIndx[fc_i])
			return PointerGetDatum(construct_empty_array(fc_elemtype));
	}

	mda_get_range(fc_ndim, fc_span, fc_lowerIndx, fc_upperIndx);

	fc_bytes = fc_array_slice_size(fc_arraydataptr, fc_arraynullsptr,
							 fc_ndim, fc_dim, fc_lb,
							 fc_lowerIndx, fc_upperIndx,
							 fc_elmlen, fc_elmbyval, fc_elmalign);

	/*
	 * 当前，如果源数据具有空位图，我们将在结果中放入一个空位图；
	 * 可以聪明一点...
	 */
	if (fc_arraynullsptr)
	{
		fc_dataoffset = ARR_OVERHEAD_WITHNULLS(fc_ndim, ArrayGetNItems(fc_ndim, fc_span));
		fc_bytes += fc_dataoffset;
	}
	else
	{
		fc_dataoffset = 0;			/* 表示没有空位图的标记 */
		fc_bytes += ARR_OVERHEAD_NONULLS(fc_ndim);
	}

	fc_newarray = (ArrayType *) palloc0(fc_bytes);
	SET_VARSIZE(fc_newarray, fc_bytes);
	fc_newarray->ndim = fc_ndim;
	fc_newarray->dataoffset = fc_dataoffset;
	fc_newarray->elemtype = fc_elemtype;
	memcpy(ARR_DIMS(fc_newarray), fc_span, fc_ndim * sizeof(int));

	/*
	 * 新数组的下界设置为 1。以前（在 7.3 之前）我们
	 * 拷贝了给定的 lowerIndx 值...但这似乎令人困惑。
	 */
	fc_newlb = ARR_LBOUND(fc_newarray);
	for (fc_i = 0; fc_i < fc_ndim; fc_i++)
		fc_newlb[fc_i] = 1;

	fc_array_extract_slice(fc_newarray,
						fc_ndim, fc_dim, fc_lb,
						fc_arraydataptr, fc_arraynullsptr,
						fc_lowerIndx, fc_upperIndx,
						fc_elmlen, fc_elmbyval, fc_elmalign);

	return PointerGetDatum(fc_newarray);
}

/*
 * array_set_element :
 *		  此例程将一个数组元素（由下标数组指定）的值设置为
 *		  由“dataValue”指定的新值。
 *
 * 此功能处理普通的 varlena 数组和固定长度数组。
 *
 * 输入：
 *	arraydatum: 初始数组对象（不能为 NULL）
 *	nSubscripts: 提供的下标数
 *	indx[]: 下标值
 *	dataValue: 要插入到给定位置的 datum
 *	isNull: dataValue 是否为 NULL
 *	arraytyplen: 数组类型的 pg_type.typlen
 *	elmlen: 数组元素类型的 pg_type.typlen
 *	elmbyval: 数组元素类型的 pg_type.typbyval
 *	elmalign: 数组元素类型的 pg_type.typalign
 *
 * 结果：
 *		  返回一个新数组，与旧数组 except for the one
 *		  被修改的条目。原始数组对象不变，
 *		  除非传递的是对扩展数组对象的读写引用；在这种情况下，扩展数组在原位更新。
 *
 * 对于一维数组，我们允许通过将值分配给现有下标范围
 * 之外的位置来扩展数组；现有元素与新元素之间的任何位置都设置为 NULL。
 * （XXX TODO：允许这种行为对应于多维数组）
 *
 * 注意：对于赋值，我们在无效的下标等情况下抛出错误，
 * 而不是像取回操作那样返回 NULL。
 */
Datum array_set_element(Datum fc_arraydatum,
				  int fc_nSubscripts,
				  int *fc_indx,
				  Datum fc_dataValue,
				  bool fc_isNull,
				  int fc_arraytyplen,
				  int fc_elmlen,
				  bool fc_elmbyval,
				  char fc_elmalign)
{
	ArrayType  *fc_array;
	ArrayType  *fc_newarray;
	int			fc_i,
				fc_ndim,
				fc_dim[MAXDIM],
				fc_lb[MAXDIM],
				fc_offset;
	char	   *fc_elt_ptr;
	bool		fc_newhasnulls;
	bits8	   *fc_oldnullbitmap;
	int			fc_oldnitems,
				fc_newnitems,
				fc_olddatasize,
				fc_newsize,
				fc_olditemlen,
				fc_newitemlen,
				fc_overheadlen,
				fc_oldoverheadlen,
				fc_addedbefore,
				fc_addedafter,
				fc_lenbefore,
				fc_lenafter;

	if (fc_arraytyplen > 0)
	{
		
/*
		 * 定长数组 - 这些假定为一维，从零开始。我们
		 * 也不能扩展它们。
		 */
		char	   *fc_resultarray;

		if (fc_nSubscripts != 1)
			ereport(ERROR,
					(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
					 errmsg("wrong number of array subscripts")));

		if (fc_indx[0] < 0 || fc_indx[0] >= fc_arraytyplen / fc_elmlen)
			ereport(ERROR,
					(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
					 errmsg("array subscript out of range")));

		if (fc_isNull)
			ereport(ERROR,
					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
					 errmsg("cannot assign null value to an element of a fixed-length array")));

		fc_resultarray = (char *) palloc(fc_arraytyplen);
		memcpy(fc_resultarray, DatumGetPointer(fc_arraydatum), fc_arraytyplen);
		fc_elt_ptr = (char *) fc_resultarray + fc_indx[0] * fc_elmlen;
		fc_ArrayCastAndSet(fc_dataValue, fc_elmlen, fc_elmbyval, fc_elmalign, fc_elt_ptr);
		return PointerGetDatum(fc_resultarray);
	}

	if (fc_nSubscripts <= 0 || fc_nSubscripts > MAXDIM)
		ereport(ERROR,
				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
				 errmsg("wrong number of array subscripts")));

	/* 确保要插入的项没有被烤焦 */
	if (fc_elmlen == -1 && !fc_isNull)
		fc_dataValue = PointerGetDatum(PG_DETOAST_DATUM(fc_dataValue));

	if (VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(fc_arraydatum)))
	{
		/* 扩展数组：让我们在一个单独的函数中完成此操作 */
		return fc_array_set_element_expanded(fc_arraydatum,
										  fc_nSubscripts,
										  fc_indx,
										  fc_dataValue,
										  fc_isNull,
										  fc_arraytyplen,
										  fc_elmlen,
										  fc_elmbyval,
										  fc_elmalign);
	}

	/* 如有必要，请解压输入数组 */
	fc_array = DatumGetArrayTypeP(fc_arraydatum);

	fc_ndim = ARR_NDIM(fc_array);

	/*
	 * 如果维数为零，即为空数组，则创建一个具有
	 * nSubscripts 维度的数组，并将下界设置为提供的
	 * 下标。
	 */
	if (fc_ndim == 0)
	{
		Oid			fc_elmtype = ARR_ELEMTYPE(fc_array);

		for (fc_i = 0; fc_i < fc_nSubscripts; fc_i++)
		{
			fc_dim[fc_i] = 1;
			fc_lb[fc_i] = fc_indx[fc_i];
		}

		return PointerGetDatum(construct_md_array(&fc_dataValue, &fc_isNull,
												  fc_nSubscripts, fc_dim, fc_lb,
												  fc_elmtype,
												  fc_elmlen, fc_elmbyval, fc_elmalign));
	}

	if (fc_ndim != fc_nSubscripts)
		ereport(ERROR,
				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
				 errmsg("wrong number of array subscripts")));

	/* 复制 dim/lb，因为我们可能会修改它们 */
	memcpy(fc_dim, ARR_DIMS(fc_array), fc_ndim * sizeof(int));
	memcpy(fc_lb, ARR_LBOUND(fc_array), fc_ndim * sizeof(int));

	fc_newhasnulls = (ARR_HASNULL(fc_array) || fc_isNull);
	fc_addedbefore = fc_addedafter = 0;

	/*
	 * 检查下标。我们假定已存在的下标通过了
	 * ArrayCheckBounds，因此可以在不溢出的情况下计算 dim[i] + lb[i]。
	 * 但我们必须小心在计算新 dim[] 值时出现的其他溢出。
	 */
	if (fc_ndim == 1)
	{
		if (fc_indx[0] < fc_lb[0])
		{
			/* addedbefore = lb[0] - indx[0]; */
			/* dim[0] += addedbefore; */
			if (pg_sub_s32_overflow(fc_lb[0], fc_indx[0], &fc_addedbefore) ||
				pg_add_s32_overflow(fc_dim[0], fc_addedbefore, &fc_dim[0]))
				ereport(ERROR,
						(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
						 errmsg("array size exceeds the maximum allowed (%d)",
								(int) MaxArraySize)));
			fc_lb[0] = fc_indx[0];
			if (fc_addedbefore > 1)
				fc_newhasnulls = true; /* 将插入空值 */
		}
		if (fc_indx[0] >= (fc_dim[0] + fc_lb[0]))
		{
			/* addedafter = indx[0] - (dim[0] + lb[0]) + 1; */
			/* dim[0] += addedafter; */
			if (pg_sub_s32_overflow(fc_indx[0], fc_dim[0] + fc_lb[0], &fc_addedafter) ||
				pg_add_s32_overflow(fc_addedafter, 1, &fc_addedafter) ||
				pg_add_s32_overflow(fc_dim[0], fc_addedafter, &fc_dim[0]))
				ereport(ERROR,
						(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
						 errmsg("array size exceeds the maximum allowed (%d)",
								(int) MaxArraySize)));
			if (fc_addedafter > 1)
				fc_newhasnulls = true; /* 将插入空值 */
		}
	}
	else
	{
		/*
		 * XXX 目前我们不支持在赋值期间扩展多维数组
		 */
		for (fc_i = 0; fc_i < fc_ndim; fc_i++)
		{
			if (fc_indx[fc_i] < fc_lb[fc_i] ||
				fc_indx[fc_i] >= (fc_dim[fc_i] + fc_lb[fc_i]))
				ereport(ERROR,
						(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
						 errmsg("array subscript out of range")));
		}
	}

	/* 这检查数组维度的溢出 */
	fc_newnitems = ArrayGetNItems(fc_ndim, fc_dim);
	ArrayCheckBounds(fc_ndim, fc_dim, fc_lb);

	/*
	 * 计算要复制的项和区域的大小
	 */
	if (fc_newhasnulls)
		fc_overheadlen = ARR_OVERHEAD_WITHNULLS(fc_ndim, fc_newnitems);
	else
		fc_overheadlen = ARR_OVERHEAD_NONULLS(fc_ndim);
	fc_oldnitems = ArrayGetNItems(fc_ndim, ARR_DIMS(fc_array));
	fc_oldnullbitmap = ARR_NULLBITMAP(fc_array);
	fc_oldoverheadlen = ARR_DATA_OFFSET(fc_array);
	fc_olddatasize = ARR_SIZE(fc_array) - fc_oldoverheadlen;
	if (fc_addedbefore)
	{
		fc_offset = 0;
		fc_lenbefore = 0;
		fc_olditemlen = 0;
		fc_lenafter = fc_olddatasize;
	}
	else if (fc_addedafter)
	{
		fc_offset = fc_oldnitems;
		fc_lenbefore = fc_olddatasize;
		fc_olditemlen = 0;
		fc_lenafter = 0;
	}
	else
	{
		fc_offset = ArrayGetOffset(fc_nSubscripts, fc_dim, fc_lb, fc_indx);
		fc_elt_ptr = fc_array_seek(ARR_DATA_PTR(fc_array), 0, fc_oldnullbitmap, fc_offset,
							 fc_elmlen, fc_elmbyval, fc_elmalign);
		fc_lenbefore = (int) (fc_elt_ptr - ARR_DATA_PTR(fc_array));
		if (fc_array_get_isnull(fc_oldnullbitmap, fc_offset))
			fc_olditemlen = 0;
		else
		{
			fc_olditemlen = att_addlength_pointer(0, fc_elmlen, fc_elt_ptr);
			fc_olditemlen = att_align_nominal(fc_olditemlen, fc_elmalign);
		}
		fc_lenafter = (int) (fc_olddatasize - fc_lenbefore - fc_olditemlen);
	}

	if (fc_isNull)
		fc_newitemlen = 0;
	else
	{
		fc_newitemlen = att_addlength_datum(0, fc_elmlen, fc_dataValue);
		fc_newitemlen = att_align_nominal(fc_newitemlen, fc_elmalign);
	}

	fc_newsize = fc_overheadlen + fc_lenbefore + fc_newitemlen + fc_lenafter;

	/*
	 * 好的，创建新的数组并填写头部/维度
	 */
	fc_newarray = (ArrayType *) palloc0(fc_newsize);
	SET_VARSIZE(fc_newarray, fc_newsize);
	fc_newarray->ndim = fc_ndim;
	fc_newarray->dataoffset = fc_newhasnulls ? fc_overheadlen : 0;
	fc_newarray->elemtype = ARR_ELEMTYPE(fc_array);
	memcpy(ARR_DIMS(fc_newarray), fc_dim, fc_ndim * sizeof(int));
	memcpy(ARR_LBOUND(fc_newarray), fc_lb, fc_ndim * sizeof(int));

	/*
	 * 填充数据
	 */
	memcpy((char *) fc_newarray + fc_overheadlen,
		   (char *) fc_array + fc_oldoverheadlen,
		   fc_lenbefore);
	if (!fc_isNull)
		fc_ArrayCastAndSet(fc_dataValue, fc_elmlen, fc_elmbyval, fc_elmalign,
						(char *) fc_newarray + fc_overheadlen + fc_lenbefore);
	memcpy((char *) fc_newarray + fc_overheadlen + fc_lenbefore + fc_newitemlen,
		   (char *) fc_array + fc_oldoverheadlen + fc_lenbefore + fc_olditemlen,
		   fc_lenafter);

	/*
	 * 如有必要，填充 null 位图
	 *
	 * 注意：可能我们只是将最后一个 NULL 替换为非 NULL，
	 * 从而可以去掉位图。然而，似乎没有必要进行这种测试。
	 */
	if (fc_newhasnulls)
	{
		bits8	   *fc_newnullbitmap = ARR_NULLBITMAP(fc_newarray);

		/* palloc0 以上已将任何插入位置标记为 null */
		/* 修正插入的值 */
		if (fc_addedafter)
			fc_array_set_isnull(fc_newnullbitmap, fc_newnitems - 1, fc_isNull);
		else
			fc_array_set_isnull(fc_newnullbitmap, fc_offset, fc_isNull);
		/* 修正复制的范围 */
		if (fc_addedbefore)
			array_bitmap_copy(fc_newnullbitmap, fc_addedbefore,
							  fc_oldnullbitmap, 0,
							  fc_oldnitems);
		else
		{
			array_bitmap_copy(fc_newnullbitmap, 0,
							  fc_oldnullbitmap, 0,
							  fc_offset);
			if (fc_addedafter == 0)
				array_bitmap_copy(fc_newnullbitmap, fc_offset + 1,
								  fc_oldnullbitmap, fc_offset + 1,
								  fc_oldnitems - fc_offset - 1);
		}
	}

	return PointerGetDatum(fc_newarray);
}

/*
 * 扩展数组的 array_set_element() 实现
 *
 * 注意：与对可读/写扩展对象的任何操作一样，我们必须
 * 小心，不让对象在部分失败时处于损坏状态。
 */
static Datum fc_array_set_element_expanded(Datum fc_arraydatum,
						   int fc_nSubscripts, int *fc_indx,
						   Datum fc_dataValue, bool fc_isNull,
						   int fc_arraytyplen,
						   int fc_elmlen, bool fc_elmbyval, char fc_elmalign)
{
	ExpandedArrayHeader *fc_eah;
	Datum	   *fc_dvalues;
	bool	   *fc_dnulls;
	int			fc_i,
				fc_ndim,
				fc_dim[MAXDIM],
				fc_lb[MAXDIM],
				fc_offset;
	bool		fc_dimschanged,
				fc_newhasnulls;
	int			fc_addedbefore,
				fc_addedafter;
	char	   *fc_oldValue;

	/* 如果尚未如此，则转换为 R/W 对象 */
	fc_eah = DatumGetExpandedArray(fc_arraydatum);

	/* 将调用者的信息与对象进行合理性检查；否则我们不使用它 */
	Assert(fc_arraytyplen == -1);
	Assert(fc_elmlen == fc_eah->typlen);
	Assert(fc_elmbyval == fc_eah->typbyval);
	Assert(fc_elmalign == fc_eah->typalign);

	/*
	 * 将维度信息复制到本地存储。这使我们能够在
	 * 需要时修改维度，而不会在失败时弄乱扩展值。
	 */
	fc_ndim = fc_eah->ndims;
	Assert(fc_ndim >= 0 && fc_ndim <= MAXDIM);
	memcpy(fc_dim, fc_eah->dims, fc_ndim * sizeof(int));
	memcpy(fc_lb, fc_eah->lbound, fc_ndim * sizeof(int));
	fc_dimschanged = false;

	/*
	 * 如果维数为零，即为空数组，则创建一个具有
	 * nSubscripts 维度的数组，并将下界设置为提供的
	 * 下标。
	 */
	if (fc_ndim == 0)
	{
		/*
		 * 为新的维度信息分配足够的空间。如果我们后面失败，
		 * 这也无妨。
		 */
		Assert(fc_nSubscripts > 0 && fc_nSubscripts <= MAXDIM);
		fc_eah->dims = (int *) MemoryContextAllocZero(fc_eah->hdr.eoh_context,
												   fc_nSubscripts * sizeof(int));
		fc_eah->lbound = (int *) MemoryContextAllocZero(fc_eah->hdr.eoh_context,
													 fc_nSubscripts * sizeof(int));

		/* 更新维度信息的本地副本 */
		fc_ndim = fc_nSubscripts;
		for (fc_i = 0; fc_i < fc_nSubscripts; fc_i++)
		{
			fc_dim[fc_i] = 0;
			fc_lb[fc_i] = fc_indx[fc_i];
		}
		fc_dimschanged = true;
	}
	else if (fc_ndim != fc_nSubscripts)
		ereport(ERROR,
				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
				 errmsg("wrong number of array subscripts")));

	/*
	 * 如果我们尚未这样做，则解构数组。（总有一天可以为
	 * 固定长度、无空值的情况添加特例路径，我们可以
	 * 在不解构的情况下就地覆盖一个元素。但今天不是那一天。）
	 */
	deconstruct_expanded_array(fc_eah);

	/*
	 * 如果需要，将新元素复制到数组的上下文中（我们假定它
	 * 已经解冻，因此不应创建任何垃圾）。在我们进行任何
	 * 重大更改之前执行此操作确保我们的行为是合理的，即使
	 * 源是对这个同一数组某个元素的引用。如果我们在后面失败，
	 * 这段内存会泄露，但这也并不算太严重。
	 */
	if (!fc_eah->typbyval && !fc_isNull)
	{
		MemoryContext fc_oldcxt = MemoryContextSwitchTo(fc_eah->hdr.eoh_context);

		fc_dataValue = datumCopy(fc_dataValue, false, fc_eah->typlen);
		MemoryContextSwitchTo(fc_oldcxt);
	}

	fc_dvalues = fc_eah->dvalues;
	fc_dnulls = fc_eah->dnulls;

	fc_newhasnulls = ((fc_dnulls != NULL) || fc_isNull);
	fc_addedbefore = fc_addedafter = 0;

	/*
	 * 检查下标（此逻辑必须与 array_set_element 匹配）。我们假定
	 * 已存在的下标通过了 ArrayCheckBounds，因此可以在不溢出的情况下计算
	 * dim[i] + lb[i]。但我们必须小心在计算新 dim[] 值时出现的其他
	 * 溢出。
	 */
	if (fc_ndim == 1)
	{
		if (fc_indx[0] < fc_lb[0])
		{
			/* addedbefore = lb[0] - indx[0]; */
			/* dim[0] += addedbefore; */
			if (pg_sub_s32_overflow(fc_lb[0], fc_indx[0], &fc_addedbefore) ||
				pg_add_s32_overflow(fc_dim[0], fc_addedbefore, &fc_dim[0]))
				ereport(ERROR,
						(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
						 errmsg("array size exceeds the maximum allowed (%d)",
								(int) MaxArraySize)));
			fc_lb[0] = fc_indx[0];
			fc_dimschanged = true;
			if (fc_addedbefore > 1)
				fc_newhasnulls = true; /* 将插入空值 */
		}
		if (fc_indx[0] >= (fc_dim[0] + fc_lb[0]))
		{
			/* addedafter = indx[0] - (dim[0] + lb[0]) + 1; */
			/* dim[0] += addedafter; */
			if (pg_sub_s32_overflow(fc_indx[0], fc_dim[0] + fc_lb[0], &fc_addedafter) ||
				pg_add_s32_overflow(fc_addedafter, 1, &fc_addedafter) ||
				pg_add_s32_overflow(fc_dim[0], fc_addedafter, &fc_dim[0]))
				ereport(ERROR,
						(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
						 errmsg("array size exceeds the maximum allowed (%d)",
								(int) MaxArraySize)));
			fc_dimschanged = true;
			if (fc_addedafter > 1)
				fc_newhasnulls = true; /* 将插入空值 */
		}
	}
	else
	{
		/*
		 * XXX 目前我们不支持在赋值期间扩展多维数组
		 */
		for (fc_i = 0; fc_i < fc_ndim; fc_i++)
		{
			if (fc_indx[fc_i] < fc_lb[fc_i] ||
				fc_indx[fc_i] >= (fc_dim[fc_i] + fc_lb[fc_i]))
				ereport(ERROR,
						(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
						 errmsg("array subscript out of range")));
		}
	}

	/* 检查数组维度的溢出 */
	if (fc_dimschanged)
	{
		(void) ArrayGetNItems(fc_ndim, fc_dim);
		ArrayCheckBounds(fc_ndim, fc_dim, fc_lb);
	}

	/* 现在我们可以计算数组中目标项的线性偏移量 */
	fc_offset = ArrayGetOffset(fc_nSubscripts, fc_dim, fc_lb, fc_indx);

	/* 如有需要，物理上扩大现有 dvalues/dnulls 数组 */
	if (fc_dim[0] > fc_eah->dvalueslen)
	{
		/* 如果我们正在扩大，想要一些额外的空间 */
		int			fc_newlen = fc_dim[0] + fc_dim[0] / 8;

		fc_newlen = Max(fc_newlen, fc_dim[0]);	/* 整数溢出保护 */
		fc_eah->dvalues = fc_dvalues = (Datum *)
			repalloc(fc_dvalues, fc_newlen * sizeof(Datum));
		if (fc_dnulls)
			fc_eah->dnulls = fc_dnulls = (bool *)
				repalloc(fc_dnulls, fc_newlen * sizeof(bool));
		fc_eah->dvalueslen = fc_newlen;
	}

	/*
	 * 如果我们需要一个空位图并且还没有一个，创建它，确保
	 * 将所有现有条目标记为非空。
	 */
	if (fc_newhasnulls && fc_dnulls == NULL)
		fc_eah->dnulls = fc_dnulls = (bool *)
			MemoryContextAllocZero(fc_eah->hdr.eoh_context,
								   fc_eah->dvalueslen * sizeof(bool));

	/*
	 * 我们现在已经分配了所有所需的空间，所以我们准备
	 * 进行不可逆的更改。对下面的失败保持高度警惕。
	 */

	/* 扁平化的值将不再准确地表示数组 */
	fc_eah->fvalue = NULL;
	/* 而且我们也不知道扁平化的大小 */
	fc_eah->flat_size = 0;

	/* 如果需要，更新维度信息 */
	if (fc_dimschanged)
	{
		fc_eah->ndims = fc_ndim;
		memcpy(fc_eah->dims, fc_dim, fc_ndim * sizeof(int));
		memcpy(fc_eah->lbound, fc_lb, fc_ndim * sizeof(int));
	}

	/* 如果需要，重新定位项目，并用空值填充添加之前的项目 */
	if (fc_addedbefore > 0)
	{
		memmove(fc_dvalues + fc_addedbefore, fc_dvalues, fc_eah->nelems * sizeof(Datum));
		for (fc_i = 0; fc_i < fc_addedbefore; fc_i++)
			fc_dvalues[fc_i] = (Datum) 0;
		if (fc_dnulls)
		{
			memmove(fc_dnulls + fc_addedbefore, fc_dnulls, fc_eah->nelems * sizeof(bool));
			for (fc_i = 0; fc_i < fc_addedbefore; fc_i++)
				fc_dnulls[fc_i] = true;
		}
		fc_eah->nelems += fc_addedbefore;
	}

	/* 用空值填充添加之后的项目 */
	if (fc_addedafter > 0)
	{
		for (fc_i = 0; fc_i < fc_addedafter; fc_i++)
			fc_dvalues[fc_eah->nelems + fc_i] = (Datum) 0;
		if (fc_dnulls)
		{
			for (fc_i = 0; fc_i < fc_addedafter; fc_i++)
				fc_dnulls[fc_eah->nelems + fc_i] = true;
		}
		fc_eah->nelems += fc_addedafter;
	}

	/* 获取旧元素值以便进行pfree，如果需要。 */
	if (!fc_eah->typbyval && (fc_dnulls == NULL || !fc_dnulls[fc_offset]))
		fc_oldValue = (char *) DatumGetPointer(fc_dvalues[fc_offset]);
	else
		fc_oldValue = NULL;

	/* 最后我们可以插入新的元素。 */
	fc_dvalues[fc_offset] = fc_dataValue;
	if (fc_dnulls)
		fc_dnulls[fc_offset] = fc_isNull;

	/*
	 * 如果需要，释放旧元素; 这可以防止重复的元素替换
	 * 膨胀数组的存储。如果pfree以某种方式失败，它
	 * 不会破坏数组。
	 */
	if (fc_oldValue)
	{
		/* 不要尝试pfree原始扁平数组的一部分 */
		if (fc_oldValue < fc_eah->fstartptr || fc_oldValue >= fc_eah->fendptr)
			pfree(fc_oldValue);
	}

	/* 完成，返回对象的标准TOAST指针 */
	return EOHPGetRWDatum(&fc_eah->hdr);
}

/*
 * array_set_slice :
 *      此例程将一系列数组位置（由上和下下标值指定）的值
 *      设置为另一个数组传递的新值。
 *
 * 这适用于普通的varlena数组和固定长度的数组。
 *
 * 输入：
 *  arraydatum：初始数组对象（不能为空）
 *  nSubscripts：提供的下标数量（必须对上/下相同）
 *  upperIndx[]：上下标值
 *  lowerIndx[]：下下标值
 *  upperProvided[]：提供的上下标值为true
 *  lowerProvided[]：提供的下下标值为true
 *  srcArrayDatum：插入值的来源
 *  isNull：指示srcArrayDatum是否为NULL
 *  arraytyplen：数组类型的pg_type.typlen
 *  elmlen：数组元素类型的pg_type.typlen
 *  elmbyval：数组元素类型的pg_type.typbyval
 *  elmalign：数组元素类型的pg_type.typalign
 *
 * 结果：
 *      返回一个新数组，就像旧数组一样，除了已修改的范围。
 *      原始数组对象未更改。
 *
 * 省略的上和下标值由相应的
 * 数组边界替代。
 *
 * 仅对于一维数组，我们允许通过对现有下标范围外的
 * 位置进行赋值来扩展数组；任何
 * 现有元素与新元素之间的位置都设置为NULL。
 * （XXX TODO：允许多维数组的相应行为）
 *
 * 注意：我们假设可以随意修改提供的索引数组
 * lowerIndx[]和upperIndx[]；此外，当nSubscripts较少时，这些数组的大小必须为MAXDIM。
 * 这些通常只是临时数组。
 *
 * 注意：对于赋值，我们对愚蠢的下标等抛出错误，
 * 而不是像获取操作那样返回NULL或空数组。
 */
Datum array_set_slice(Datum fc_arraydatum,
				int fc_nSubscripts,
				int *fc_upperIndx,
				int *fc_lowerIndx,
				bool *fc_upperProvided,
				bool *fc_lowerProvided,
				Datum fc_srcArrayDatum,
				bool fc_isNull,
				int fc_arraytyplen,
				int fc_elmlen,
				bool fc_elmbyval,
				char fc_elmalign)
{
	ArrayType  *fc_array;
	ArrayType  *fc_srcArray;
	ArrayType  *fc_newarray;
	int			fc_i,
				fc_ndim,
				fc_dim[MAXDIM],
				fc_lb[MAXDIM],
				fc_span[MAXDIM];
	bool		fc_newhasnulls;
	int			fc_nitems,
				fc_nsrcitems,
				fc_olddatasize,
				fc_newsize,
				fc_olditemsize,
				fc_newitemsize,
				fc_overheadlen,
				fc_oldoverheadlen,
				fc_addedbefore,
				fc_addedafter,
				fc_lenbefore,
				fc_lenafter,
				fc_itemsbefore,
				fc_itemsafter,
				fc_nolditems;

	/* 目前，从NULL源数组的赋值是无操作 */
	if (fc_isNull)
		return fc_arraydatum;

	if (fc_arraytyplen > 0)
	{
		/*
		 * 固定长度数组 -- 暂时还没有进行这项工作...
		 */
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("updates on slices of fixed-length arrays not implemented")));
	}

	/* 如果必要，取消脱托管数组 */
	fc_array = DatumGetArrayTypeP(fc_arraydatum);
	fc_srcArray = DatumGetArrayTypeP(fc_srcArrayDatum);

	/* 注意：我们假设srcArray不包含任何已脱托管元素 */

	fc_ndim = ARR_NDIM(fc_array);

	/*
	 * 如果维度数量为零，即空数组，创建一个具有
	 * nSubscripts维度的数组，并将上限和下限设置为
	 * 提供的下标
	 */
	if (fc_ndim == 0)
	{
		Datum	   *fc_dvalues;
		bool	   *fc_dnulls;
		int			fc_nelems;
		Oid			fc_elmtype = ARR_ELEMTYPE(fc_array);

		deconstruct_array(fc_srcArray, fc_elmtype, fc_elmlen, fc_elmbyval, fc_elmalign,
						  &fc_dvalues, &fc_dnulls, &fc_nelems);

		for (fc_i = 0; fc_i < fc_nSubscripts; fc_i++)
		{
			if (!fc_upperProvided[fc_i] || !fc_lowerProvided[fc_i])
				ereport(ERROR,
						(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
						 errmsg("array slice subscript must provide both boundaries"),
						 errdetail("When assigning to a slice of an empty array value,"
								   " slice boundaries must be fully specified.")));

			/* 计算“upperIndx[i] - lowerIndx[i] + 1”，检测溢出 */
			if (pg_sub_s32_overflow(fc_upperIndx[fc_i], fc_lowerIndx[fc_i], &fc_dim[fc_i]) ||
				pg_add_s32_overflow(fc_dim[fc_i], 1, &fc_dim[fc_i]))
				ereport(ERROR,
						(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
						 errmsg("array size exceeds the maximum allowed (%d)",
								(int) MaxArraySize)));

			fc_lb[fc_i] = fc_lowerIndx[fc_i];
		}

		/* 投诉如果源项目太少；但是我们忽略多余的内容 */
		if (fc_nelems < ArrayGetNItems(fc_nSubscripts, fc_dim))
			ereport(ERROR,
					(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
					 errmsg("source array too small")));

		return PointerGetDatum(construct_md_array(fc_dvalues, fc_dnulls, fc_nSubscripts,
												  fc_dim, fc_lb, fc_elmtype,
												  fc_elmlen, fc_elmbyval, fc_elmalign));
	}

	if (fc_ndim < fc_nSubscripts || fc_ndim <= 0 || fc_ndim > MAXDIM)
		ereport(ERROR,
				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
				 errmsg("wrong number of array subscripts")));

	/* 复制 dim/lb，因为我们可能会修改它们 */
	memcpy(fc_dim, ARR_DIMS(fc_array), fc_ndim * sizeof(int));
	memcpy(fc_lb, ARR_LBOUND(fc_array), fc_ndim * sizeof(int));

	fc_newhasnulls = (ARR_HASNULL(fc_array) || ARR_HASNULL(fc_srcArray));
	fc_addedbefore = fc_addedafter = 0;

	/*
	 * 检查下标。我们假定已存在的下标通过了
	 * ArrayCheckBounds，因此可以在不溢出的情况下计算 dim[i] + lb[i]。
	 * 但我们必须小心在计算新 dim[] 值时出现的其他溢出。
	 */
	if (fc_ndim == 1)
	{
		Assert(fc_nSubscripts == 1);
		if (!fc_lowerProvided[0])
			fc_lowerIndx[0] = fc_lb[0];
		if (!fc_upperProvided[0])
			fc_upperIndx[0] = fc_dim[0] + fc_lb[0] - 1;
		if (fc_lowerIndx[0] > fc_upperIndx[0])
			ereport(ERROR,
					(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
					 errmsg("upper bound cannot be less than lower bound")));
		if (fc_lowerIndx[0] < fc_lb[0])
		{
			/* addedbefore = lb[0] - lowerIndx[0]; */
			/* dim[0] += addedbefore; */
			if (pg_sub_s32_overflow(fc_lb[0], fc_lowerIndx[0], &fc_addedbefore) ||
				pg_add_s32_overflow(fc_dim[0], fc_addedbefore, &fc_dim[0]))
				ereport(ERROR,
						(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
						 errmsg("array size exceeds the maximum allowed (%d)",
								(int) MaxArraySize)));
			fc_lb[0] = fc_lowerIndx[0];
			if (fc_addedbefore > 1)
				fc_newhasnulls = true; /* 将插入空值 */
		}
		if (fc_upperIndx[0] >= (fc_dim[0] + fc_lb[0]))
		{
			/* addedafter = upperIndx[0] - (dim[0] + lb[0]) + 1; */
			/* dim[0] += addedafter; */
			if (pg_sub_s32_overflow(fc_upperIndx[0], fc_dim[0] + fc_lb[0], &fc_addedafter) ||
				pg_add_s32_overflow(fc_addedafter, 1, &fc_addedafter) ||
				pg_add_s32_overflow(fc_dim[0], fc_addedafter, &fc_dim[0]))
				ereport(ERROR,
						(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
						 errmsg("array size exceeds the maximum allowed (%d)",
								(int) MaxArraySize)));
			if (fc_addedafter > 1)
				fc_newhasnulls = true; /* 将插入空值 */
		}
	}
	else
	{
		/*
		 * XXX 目前我们不支持在赋值期间扩展多维数组
		 */
		for (fc_i = 0; fc_i < fc_nSubscripts; fc_i++)
		{
			if (!fc_lowerProvided[fc_i])
				fc_lowerIndx[fc_i] = fc_lb[fc_i];
			if (!fc_upperProvided[fc_i])
				fc_upperIndx[fc_i] = fc_dim[fc_i] + fc_lb[fc_i] - 1;
			if (fc_lowerIndx[fc_i] > fc_upperIndx[fc_i])
				ereport(ERROR,
						(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
						 errmsg("upper bound cannot be less than lower bound")));
			if (fc_lowerIndx[fc_i] < fc_lb[fc_i] ||
				fc_upperIndx[fc_i] >= (fc_dim[fc_i] + fc_lb[fc_i]))
				ereport(ERROR,
						(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
						 errmsg("array subscript out of range")));
		}
		/* 用完整数组范围填充任何缺失的下标位置 */
		for (; fc_i < fc_ndim; fc_i++)
		{
			fc_lowerIndx[fc_i] = fc_lb[fc_i];
			fc_upperIndx[fc_i] = fc_dim[fc_i] + fc_lb[fc_i] - 1;
			if (fc_lowerIndx[fc_i] > fc_upperIndx[fc_i])
				ereport(ERROR,
						(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
						 errmsg("upper bound cannot be less than lower bound")));
		}
	}

	/* 主要做这个是为了检查是否溢出 */
	fc_nitems = ArrayGetNItems(fc_ndim, fc_dim);
	ArrayCheckBounds(fc_ndim, fc_dim, fc_lb);

	/*
	 * 确保源数组有足够的条目。注意我们忽略源数组的形状，
	 * 只是串行读取条目。
	 */
	mda_get_range(fc_ndim, fc_span, fc_lowerIndx, fc_upperIndx);
	fc_nsrcitems = ArrayGetNItems(fc_ndim, fc_span);
	if (fc_nsrcitems > ArrayGetNItems(ARR_NDIM(fc_srcArray), ARR_DIMS(fc_srcArray)))
		ereport(ERROR,
				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
				 errmsg("source array too small")));

	/*
	 * 计算新条目占用的空间，替换条目占用的空间，以及新数组所需的空间。
	 */
	if (fc_newhasnulls)
		fc_overheadlen = ARR_OVERHEAD_WITHNULLS(fc_ndim, fc_nitems);
	else
		fc_overheadlen = ARR_OVERHEAD_NONULLS(fc_ndim);
	fc_newitemsize = fc_array_nelems_size(ARR_DATA_PTR(fc_srcArray), 0,
									ARR_NULLBITMAP(fc_srcArray), fc_nsrcitems,
									fc_elmlen, fc_elmbyval, fc_elmalign);
	fc_oldoverheadlen = ARR_DATA_OFFSET(fc_array);
	fc_olddatasize = ARR_SIZE(fc_array) - fc_oldoverheadlen;
	if (fc_ndim > 1)
	{
		/*
		 * 在这里，我们不需要应对数组的扩展；如果我们必须这样做，
		 * 那会复杂得多...
		 */
		fc_olditemsize = fc_array_slice_size(ARR_DATA_PTR(fc_array),
									   ARR_NULLBITMAP(fc_array),
									   fc_ndim, fc_dim, fc_lb,
									   fc_lowerIndx, fc_upperIndx,
									   fc_elmlen, fc_elmbyval, fc_elmalign);
		fc_lenbefore = fc_lenafter = 0;	/* 保持编译器安静 */
		fc_itemsbefore = fc_itemsafter = fc_nolditems = 0;
	}
	else
	{
		/*
		 * 在这里我们必须考虑切片可能大于原始数组
		 * 和/或不与原始数组下标相邻的可能性
		 */
		int			fc_oldlb = ARR_LBOUND(fc_array)[0];
		int			fc_oldub = fc_oldlb + ARR_DIMS(fc_array)[0] - 1;
		int			fc_slicelb = Max(fc_oldlb, fc_lowerIndx[0]);
		int			fc_sliceub = Min(fc_oldub, fc_upperIndx[0]);
		char	   *fc_oldarraydata = ARR_DATA_PTR(fc_array);
		bits8	   *fc_oldarraybitmap = ARR_NULLBITMAP(fc_array);

		/* 切片之前的旧数组元素的计数/大小 */
		fc_itemsbefore = Min(fc_slicelb, fc_oldub + 1) - fc_oldlb;
		fc_lenbefore = fc_array_nelems_size(fc_oldarraydata, 0, fc_oldarraybitmap,
									  fc_itemsbefore,
									  fc_elmlen, fc_elmbyval, fc_elmalign);
		/* 被切片替换的旧数组元素的计数/大小 */
		if (fc_slicelb > fc_sliceub)
		{
			fc_nolditems = 0;
			fc_olditemsize = 0;
		}
		else
		{
			fc_nolditems = fc_sliceub - fc_slicelb + 1;
			fc_olditemsize = fc_array_nelems_size(fc_oldarraydata + fc_lenbefore,
											fc_itemsbefore, fc_oldarraybitmap,
											fc_nolditems,
											fc_elmlen, fc_elmbyval, fc_elmalign);
		}
		/* 切片之后的旧数组元素的计数/大小 */
		fc_itemsafter = fc_oldub + 1 - Max(fc_sliceub + 1, fc_oldlb);
		fc_lenafter = fc_olddatasize - fc_lenbefore - fc_olditemsize;
	}

	fc_newsize = fc_overheadlen + fc_olddatasize - fc_olditemsize + fc_newitemsize;

	fc_newarray = (ArrayType *) palloc0(fc_newsize);
	SET_VARSIZE(fc_newarray, fc_newsize);
	fc_newarray->ndim = fc_ndim;
	fc_newarray->dataoffset = fc_newhasnulls ? fc_overheadlen : 0;
	fc_newarray->elemtype = ARR_ELEMTYPE(fc_array);
	memcpy(ARR_DIMS(fc_newarray), fc_dim, fc_ndim * sizeof(int));
	memcpy(ARR_LBOUND(fc_newarray), fc_lb, fc_ndim * sizeof(int));

	if (fc_ndim > 1)
	{
		/*
		 * 在这里，我们不需要应对数组的扩展；如果我们必须这样做，
		 * 那会复杂得多...
		 */
		fc_array_insert_slice(fc_newarray, fc_array, fc_srcArray,
						   fc_ndim, fc_dim, fc_lb,
						   fc_lowerIndx, fc_upperIndx,
						   fc_elmlen, fc_elmbyval, fc_elmalign);
	}
	else
	{
		/* 填充数据 */
		memcpy((char *) fc_newarray + fc_overheadlen,
			   (char *) fc_array + fc_oldoverheadlen,
			   fc_lenbefore);
		memcpy((char *) fc_newarray + fc_overheadlen + fc_lenbefore,
			   ARR_DATA_PTR(fc_srcArray),
			   fc_newitemsize);
		memcpy((char *) fc_newarray + fc_overheadlen + fc_lenbefore + fc_newitemsize,
			   (char *) fc_array + fc_oldoverheadlen + fc_lenbefore + fc_olditemsize,
			   fc_lenafter);
		/* 如果需要，填充空值位图 */
		if (fc_newhasnulls)
		{
			bits8	   *fc_newnullbitmap = ARR_NULLBITMAP(fc_newarray);
			bits8	   *fc_oldnullbitmap = ARR_NULLBITMAP(fc_array);

			/* palloc0 以上已将任何插入位置标记为 null */
			array_bitmap_copy(fc_newnullbitmap, fc_addedbefore,
							  fc_oldnullbitmap, 0,
							  fc_itemsbefore);
			array_bitmap_copy(fc_newnullbitmap, fc_lowerIndx[0] - fc_lb[0],
							  ARR_NULLBITMAP(fc_srcArray), 0,
							  fc_nsrcitems);
			array_bitmap_copy(fc_newnullbitmap, fc_addedbefore + fc_itemsbefore + fc_nolditems,
							  fc_oldnullbitmap, fc_itemsbefore + fc_nolditems,
							  fc_itemsafter);
		}
	}

	return PointerGetDatum(fc_newarray);
}

/*
 * array_ref : 向后兼容的 array_get_element 包装器
 *
 * 这仅适用于已去烘焙/扁平化的 varlena 数组，因为数组
 * 参数被声明为 "ArrayType *"。但是，像这样的代码有足够的数量
 * 来证明保留这个 API 的必要性。
 */
Datum array_ref(ArrayType *fc_array, int fc_nSubscripts, int *fc_indx,
		  int fc_arraytyplen, int fc_elmlen, bool fc_elmbyval, char fc_elmalign,
		  bool *fc_isNull)
{
	return array_get_element(PointerGetDatum(fc_array), fc_nSubscripts, fc_indx,
							 fc_arraytyplen, fc_elmlen, fc_elmbyval, fc_elmalign,
							 fc_isNull);
}

/*
 * array_set : 向后兼容的 array_set_element 包装器
 *
 * 这仅适用于已去烘焙/扁平化的 varlena 数组，因为数组
 * 参数和结果被声明为 "ArrayType *"。但是，像这样的代码有足够的
 * 数量来证明保留这个 API 的必要性。
 */
ArrayType * array_set(ArrayType *fc_array, int fc_nSubscripts, int *fc_indx,
		  Datum fc_dataValue, bool fc_isNull,
		  int fc_arraytyplen, int fc_elmlen, bool fc_elmbyval, char fc_elmalign)
{
	return DatumGetArrayTypeP(array_set_element(PointerGetDatum(fc_array),
												fc_nSubscripts, fc_indx,
												fc_dataValue, fc_isNull,
												fc_arraytyplen,
												fc_elmlen, fc_elmbyval, fc_elmalign));
}

/*
 * array_map()
 *
 * 通过任意表达式映射数组。返回一个新数组，
 * 具有相同的维度，并且每个源元素都通过给定的、
 * 已编译的表达式进行转换。每个源元素被放置在
 * ExprState 的 innermost_caseval/innermost_casenull 字段中。
 *
 * 参数为：
 * * arrayd: 表示数组参数的 Datum。
 * * exprstate: 表示每个元素转换的 ExprState。
 * * econtext: 表达式评估的上下文。
 * * retType: 输出数组元素类型的 OID。这必须与，
 *	 或与表达式的结果类型二进制兼容。它可能
 *	 与输入数组的元素类型不同。
 * * amstate: array_map 的工作区。必须在
 *	 第一次调用之前被调用者清零，之后不再触碰。
 *
 * 在每次调用时传递一个新清零的 ArrayMapState 是合法的，
 * 但如果状态可以在一系列调用之间保持，则可以获得更好的性能。
 *
 * 注意：调用者必须确保输入数组不是 NULL。不过，
 * 数组中的 NULL 元素是可以的。
 * 注意：调用者应该在 econtext 的每元组内存上下文中运行。
 */
Datum array_map(Datum fc_arrayd,
		  ExprState *fc_exprstate, ExprContext *fc_econtext,
		  Oid fc_retType, ArrayMapState *fc_amstate)
{
	AnyArrayType *fc_v = DatumGetAnyArrayP(fc_arrayd);
	ArrayType  *fc_result;
	Datum	   *fc_values;
	bool	   *fc_nulls;
	int		   *fc_dim;
	int			fc_ndim;
	int			fc_nitems;
	int			fc_i;
	int32		fc_nbytes = 0;
	int32		fc_dataoffset;
	bool		fc_hasnulls;
	Oid			fc_inpType;
	int			fc_inp_typlen;
	bool		fc_inp_typbyval;
	char		fc_inp_typalign;
	int			fc_typlen;
	bool		fc_typbyval;
	char		fc_typalign;
	array_iter	fc_iter;
	ArrayMetaState *fc_inp_extra;
	ArrayMetaState *fc_ret_extra;
	Datum	   *fc_transform_source = fc_exprstate->innermost_caseval;
	bool	   *fc_transform_source_isnull = fc_exprstate->innermost_casenull;

	fc_inpType = AARR_ELEMTYPE(fc_v);
	fc_ndim = AARR_NDIM(fc_v);
	fc_dim = AARR_DIMS(fc_v);
	fc_nitems = ArrayGetNItems(fc_ndim, fc_dim);

	/* 检查是否为空数组 */
	if (fc_nitems <= 0)
	{
		/* 返回空数组 */
		return PointerGetDatum(construct_empty_array(fc_retType));
	}

	/*
	 * 我们安排一次只查找关于输入和返回元素类型的信息
	 * 假设元素类型在我们之下不变。
	 */
	fc_inp_extra = &fc_amstate->inp_extra;
	fc_ret_extra = &fc_amstate->ret_extra;

	if (fc_inp_extra->element_type != fc_inpType)
	{
		get_typlenbyvalalign(fc_inpType,
							 &fc_inp_extra->typlen,
							 &fc_inp_extra->typbyval,
							 &fc_inp_extra->typalign);
		fc_inp_extra->element_type = fc_inpType;
	}
	fc_inp_typlen = fc_inp_extra->typlen;
	fc_inp_typbyval = fc_inp_extra->typbyval;
	fc_inp_typalign = fc_inp_extra->typalign;

	if (fc_ret_extra->element_type != fc_retType)
	{
		get_typlenbyvalalign(fc_retType,
							 &fc_ret_extra->typlen,
							 &fc_ret_extra->typbyval,
							 &fc_ret_extra->typalign);
		fc_ret_extra->element_type = fc_retType;
	}
	fc_typlen = fc_ret_extra->typlen;
	fc_typbyval = fc_ret_extra->typbyval;
	fc_typalign = fc_ret_extra->typalign;

	/* 为新值分配临时数组 */
	fc_values = (Datum *) palloc(fc_nitems * sizeof(Datum));
	fc_nulls = (bool *) palloc(fc_nitems * sizeof(bool));

	/* 循环遍历源数据 */
	array_iter_setup(&fc_iter, fc_v);
	fc_hasnulls = false;

	for (fc_i = 0; fc_i < fc_nitems; fc_i++)
	{
		/* 获取源元素，检查是否为 NULL */
		*fc_transform_source =
			array_iter_next(&fc_iter, fc_transform_source_isnull, fc_i,
							fc_inp_typlen, fc_inp_typbyval, fc_inp_typalign);

		/* 将给定表达式应用于源元素 */
		fc_values[fc_i] = ExecEvalExpr(fc_exprstate, fc_econtext, &fc_nulls[fc_i]);

		if (fc_nulls[fc_i])
			fc_hasnulls = true;
		else
		{
			/* 确保数据未被烘烤 */
			if (fc_typlen == -1)
				fc_values[fc_i] = PointerGetDatum(PG_DETOAST_DATUM(fc_values[fc_i]));
			/* 更新总结果大小 */
			fc_nbytes = att_addlength_datum(fc_nbytes, fc_typlen, fc_values[fc_i]);
			fc_nbytes = att_align_nominal(fc_nbytes, fc_typalign);
			/* 检查总请求的溢出 */
			if (!AllocSizeIsValid(fc_nbytes))
				ereport(ERROR,
						(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
						 errmsg("array size exceeds the maximum allowed (%d)",
								(int) MaxAllocSize)));
		}
	}

	/* 分配并填充结果数组 */
	if (fc_hasnulls)
	{
		fc_dataoffset = ARR_OVERHEAD_WITHNULLS(fc_ndim, fc_nitems);
		fc_nbytes += fc_dataoffset;
	}
	else
	{
		fc_dataoffset = 0;			/* 表示没有空位图的标记 */
		fc_nbytes += ARR_OVERHEAD_NONULLS(fc_ndim);
	}
	fc_result = (ArrayType *) palloc0(fc_nbytes);
	SET_VARSIZE(fc_result, fc_nbytes);
	fc_result->ndim = fc_ndim;
	fc_result->dataoffset = fc_dataoffset;
	fc_result->elemtype = fc_retType;
	memcpy(ARR_DIMS(fc_result), AARR_DIMS(fc_v), fc_ndim * sizeof(int));
	memcpy(ARR_LBOUND(fc_result), AARR_LBOUND(fc_v), fc_ndim * sizeof(int));

	CopyArrayEls(fc_result,
				 fc_values, fc_nulls, fc_nitems,
				 fc_typlen, fc_typbyval, fc_typalign,
				 false);

	/*
	 * 注意：不要冒险尝试释放被调用表达式的结果
	 */
	pfree(fc_values);
	pfree(fc_nulls);

	return PointerGetDatum(fc_result);
}

/*
 * construct_array --- 构造数组对象的简单方法
 *
 * elems: 将成为数组内容的 Datum 项数组
 *		  （不支持 NULL 元素值）。
 * nelems: 项目数量
 * elmtype, elmlen, elmbyval, elmalign: 项目的数据类型信息
 *
 * 构造并返回一个分配的 1-D 数组对象。请注意，
 * 即使是按引用传递类型，元素值也会被复制到对象中。
 * 还请注意，如果 nelems = 0，结果将为 0-D 而不是 1-D。
 *
 * 注意：从系统目录中查找 elmlen/elmbval/elmalign 信息
 * 会更清晰，给定 elmtype。然而，调用者在多个使用中
 * 缓存此信息，或在元素类型硬编码时，能够更好地硬编码值。
 */
ArrayType * construct_array(Datum *fc_elems, int fc_nelems,
				Oid fc_elmtype,
				int fc_elmlen, bool fc_elmbyval, char fc_elmalign)
{
	int			fc_dims[1];
	int			fc_lbs[1];

	fc_dims[0] = fc_nelems;
	fc_lbs[0] = 1;

	return construct_md_array(fc_elems, NULL, 1, fc_dims, fc_lbs,
							  fc_elmtype, fc_elmlen, fc_elmbyval, fc_elmalign);
}


/************************************************************************************
 * construct_md_array --- 简单构造任意维度且可能包含NULL的数组对象的方法
 *
 * elems: 要成为数组内容的Datum项数组
 * nulls: is-null标志数组（如果没有NULL可以为NULL）
 * ndims: 维度数
 * dims: 每个维度的大小的整数数组
 * lbs: 每个维度的下界的整数数组
 * elmtype, elmlen, elmbyval, elmalign: 项的datatype信息
 *
 * 构造并返回一个palloc'd ndims-D数组对象。请注意
 * 元素值将被复制到对象中，即使是通过引用类型传递。
 * 还要注意，如果任何dims[i] = 0，结果将是0-D而不是ndims-D。
 *
 * 注意：从系统目录中查找elmlen/elmbval/elmalign信息会更加清晰，
 * 但调用者在多个使用中更适合缓存此信息，甚至如果元素类型是硬编码的
 * 也可以硬编码值。
 ************************************************************************************/
ArrayType * construct_md_array(Datum *fc_elems,
				   bool *fc_nulls,
				   int fc_ndims,
				   int *fc_dims,
				   int *fc_lbs,
				   Oid fc_elmtype, int fc_elmlen, bool fc_elmbyval, char fc_elmalign)
{
	ArrayType  *fc_result;
	bool		fc_hasnulls;
	int32		fc_nbytes;
	int32		fc_dataoffset;
	int			fc_i;
	int			fc_nelems;

	if (fc_ndims < 0)				/* 我们允许零维数组 */
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("invalid number of dimensions: %d", fc_ndims)));
	if (fc_ndims > MAXDIM)
		ereport(ERROR,
				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
				 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
						fc_ndims, MAXDIM)));

	/* 这检查数组维度的溢出 */
	fc_nelems = ArrayGetNItems(fc_ndims, fc_dims);
	ArrayCheckBounds(fc_ndims, fc_dims, fc_lbs);

	/* 如果ndims <= 0或者任何dims[i] == 0，返回空数组 */
	if (fc_nelems <= 0)
		return construct_empty_array(fc_elmtype);

	/* 计算所需空间 */
	fc_nbytes = 0;
	fc_hasnulls = false;
	for (fc_i = 0; fc_i < fc_nelems; fc_i++)
	{
		if (fc_nulls && fc_nulls[fc_i])
		{
			fc_hasnulls = true;
			continue;
		}
		/* 确保数据没有被烘烤 */
		if (fc_elmlen == -1)
			fc_elems[fc_i] = PointerGetDatum(PG_DETOAST_DATUM(fc_elems[fc_i]));
		fc_nbytes = att_addlength_datum(fc_nbytes, fc_elmlen, fc_elems[fc_i]);
		fc_nbytes = att_align_nominal(fc_nbytes, fc_elmalign);
		/* 检查总请求的溢出 */
		if (!AllocSizeIsValid(fc_nbytes))
			ereport(ERROR,
					(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
					 errmsg("array size exceeds the maximum allowed (%d)",
							(int) MaxAllocSize)));
	}

	/* 分配并初始化结果数组 */
	if (fc_hasnulls)
	{
		fc_dataoffset = ARR_OVERHEAD_WITHNULLS(fc_ndims, fc_nelems);
		fc_nbytes += fc_dataoffset;
	}
	else
	{
		fc_dataoffset = 0;			/* 表示没有空位图的标记 */
		fc_nbytes += ARR_OVERHEAD_NONULLS(fc_ndims);
	}
	fc_result = (ArrayType *) palloc0(fc_nbytes);
	SET_VARSIZE(fc_result, fc_nbytes);
	fc_result->ndim = fc_ndims;
	fc_result->dataoffset = fc_dataoffset;
	fc_result->elemtype = fc_elmtype;
	memcpy(ARR_DIMS(fc_result), fc_dims, fc_ndims * sizeof(int));
	memcpy(ARR_LBOUND(fc_result), fc_lbs, fc_ndims * sizeof(int));

	CopyArrayEls(fc_result,
				 fc_elems, fc_nulls, fc_nelems,
				 fc_elmlen, fc_elmbyval, fc_elmalign,
				 false);

	return fc_result;
}

/*
 * construct_empty_array --- 创建一个给定类型的零维数组
 */
ArrayType * construct_empty_array(Oid fc_elmtype)
{
	ArrayType  *fc_result;

	fc_result = (ArrayType *) palloc0(sizeof(ArrayType));
	SET_VARSIZE(fc_result, sizeof(ArrayType));
	fc_result->ndim = 0;
	fc_result->dataoffset = 0;
	fc_result->elemtype = fc_elmtype;
	return fc_result;
}

/*
 * construct_empty_expanded_array: 仅根据类型信息创建一个空扩展数组。
 * （如果不需要，metacache可以为NULL。）
 */
ExpandedArrayHeader * construct_empty_expanded_array(Oid fc_element_type,
							   MemoryContext fc_parentcontext,
							   ArrayMetaState *fc_metacache)
{
	ArrayType  *fc_array = construct_empty_array(fc_element_type);
	Datum		fc_d;

	fc_d = expand_array(PointerGetDatum(fc_array), fc_parentcontext, fc_metacache);
	pfree(fc_array);
	return (ExpandedArrayHeader *) DatumGetEOHP(fc_d);
}

/*
 * deconstruct_array  --- 提取数组数据的简单方法
 *
 * array: 要检查的数组对象（不能为空）
 * elmtype, elmlen, elmbyval, elmalign: 项的数据类型信息
 * elemsp: 返回值，设置为指向分配的 Datum 值的数组
 * nullsp: 返回值，设置为指向分配的 isnull 标记的数组
 * nelemsp: 返回值，设置为提取的值的数量
 *
 * 调用者可以传 nullsp == NULL 如果它不支持数组中的 NULL。
 * 注意，这会产生一个非常不具信息性的错误消息，
 * 所以只在确实不期望 NULL 的情况下使用。
 *
 * 如果数组元素是按引用传递的数据类型，返回的 Datum 将是
 * 指向数组对象中的指针。
 *
 * 注意：鉴于 elmtype，从系统目录中查找 elmlen/elmbval/elmalign 信息
 * 会更清晰。然而，在大多数当前的使用中，类型是硬编码到调用者中，
 * 因此我们也可以通过硬编码类型信息来节省查找周期。
 */
void deconstruct_array(ArrayType *fc_array,
				  Oid fc_elmtype,
				  int fc_elmlen, bool fc_elmbyval, char fc_elmalign,
				  Datum **fc_elemsp, bool **fc_nullsp, int *fc_nelemsp)
{
	Datum	   *fc_elems;
	bool	   *fc_nulls;
	int			fc_nelems;
	char	   *fc_p;
	bits8	   *fc_bitmap;
	int			fc_bitmask;
	int			fc_i;

	Assert(ARR_ELEMTYPE(fc_array) == fc_elmtype);

	fc_nelems = ArrayGetNItems(ARR_NDIM(fc_array), ARR_DIMS(fc_array));
	*fc_elemsp = fc_elems = (Datum *) palloc(fc_nelems * sizeof(Datum));
	if (fc_nullsp)
		*fc_nullsp = fc_nulls = (bool *) palloc0(fc_nelems * sizeof(bool));
	else
		fc_nulls = NULL;
	*fc_nelemsp = fc_nelems;

	fc_p = ARR_DATA_PTR(fc_array);
	fc_bitmap = ARR_NULLBITMAP(fc_array);
	fc_bitmask = 1;

	for (fc_i = 0; fc_i < fc_nelems; fc_i++)
	{
		/* 获取源元素，检查是否为 NULL */
		if (fc_bitmap && (*fc_bitmap & fc_bitmask) == 0)
		{
			fc_elems[fc_i] = (Datum) 0;
			if (fc_nulls)
				fc_nulls[fc_i] = true;
			else
				ereport(ERROR,
						(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
						 errmsg("null array element not allowed in this context")));
		}
		else
		{
			fc_elems[fc_i] = fetch_att(fc_p, fc_elmbyval, fc_elmlen);
			fc_p = att_addlength_pointer(fc_p, fc_elmlen, fc_p);
			fc_p = (char *) att_align_nominal(fc_p, fc_elmalign);
		}

		/* 如果有，向前推进位图指针 */
		if (fc_bitmap)
		{
			fc_bitmask <<= 1;
			if (fc_bitmask == 0x100)
			{
				fc_bitmap++;
				fc_bitmask = 1;
			}
		}
	}
}

/*
 * array_contains_nulls --- 检测数组是否有任何 NULL 元素
 *
 * 这会给出准确的答案，而测试 ARR_HASNULL 只告诉
 * 数组 *可能* 包含一个 NULL。
 */
bool array_contains_nulls(ArrayType *fc_array)
{
	int			fc_nelems;
	bits8	   *fc_bitmap;
	int			fc_bitmask;

	/* 如果没有 NULL 位图，简单答案 */
	if (!ARR_HASNULL(fc_array))
		return false;

	fc_nelems = ArrayGetNItems(ARR_NDIM(fc_array), ARR_DIMS(fc_array));

	fc_bitmap = ARR_NULLBITMAP(fc_array);

	/* 检查位图的整个字节，一次一个字节 */
	while (fc_nelems >= 8)
	{
		if (*fc_bitmap != 0xFF)
			return true;
		fc_bitmap++;
		fc_nelems -= 8;
	}

	/* 检查最后一个部分字节 */
	fc_bitmask = 1;
	while (fc_nelems > 0)
	{
		if ((*fc_bitmap & fc_bitmask) == 0)
			return true;
		fc_bitmask <<= 1;
		fc_nelems--;
	}

	return false;
}


/*
 * array_eq :
 *		  比较两个数组是否相等
 * result :
 *		  如果数组相等则返回 true，否则返回 false。
 *
 * 注意：我们不在这里使用 array_cmp，因为在没有总排序的情况下
 * 数据类型的相等性可能是有意义的（因此没有 btree 支持）。
 */
Datum array_eq(PG_FUNCTION_ARGS)
{
	LOCAL_FCINFO(fc_locfcinfo, 2);
	AnyArrayType *fc_array1 = PG_GETARG_ANY_ARRAY_P(0);
	AnyArrayType *fc_array2 = PG_GETARG_ANY_ARRAY_P(1);
	Oid			fc_collation = PG_GET_COLLATION();
	int			fc_ndims1 = AARR_NDIM(fc_array1);
	int			fc_ndims2 = AARR_NDIM(fc_array2);
	int		   *fc_dims1 = AARR_DIMS(fc_array1);
	int		   *fc_dims2 = AARR_DIMS(fc_array2);
	int		   *fc_lbs1 = AARR_LBOUND(fc_array1);
	int		   *fc_lbs2 = AARR_LBOUND(fc_array2);
	Oid			fc_element_type = AARR_ELEMTYPE(fc_array1);
	bool		fc_result = true;
	int			fc_nitems;
	TypeCacheEntry *fc_typentry;
	int			fc_typlen;
	bool		fc_typbyval;
	char		fc_typalign;
	array_iter	fc_it1;
	array_iter	fc_it2;
	int			fc_i;

	if (fc_element_type != AARR_ELEMTYPE(fc_array2))
		ereport(ERROR,
				(errcode(ERRCODE_DATATYPE_MISMATCH),
				 errmsg("cannot compare arrays of different element types")));

	/* 如果数组的维数不相同，则走快速路径 */
	if (fc_ndims1 != fc_ndims2 ||
		memcmp(fc_dims1, fc_dims2, fc_ndims1 * sizeof(int)) != 0 ||
		memcmp(fc_lbs1, fc_lbs2, fc_ndims1 * sizeof(int)) != 0)
		fc_result = false;
	else
	{
		/*
		 * 我们安排在每系列调用中仅查找一次相等函数，
		 * 假设元素类型在我们之下不变。使用 typcache，
		 * 以便在作为索引支持函数时没有内存泄漏。
		 */
		fc_typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
		if (fc_typentry == NULL ||
			fc_typentry->type_id != fc_element_type)
		{
			fc_typentry = lookup_type_cache(fc_element_type,
										 TYPECACHE_EQ_OPR_FINFO);
			if (!OidIsValid(fc_typentry->eq_opr_finfo.fn_oid))
				ereport(ERROR,
						(errcode(ERRCODE_UNDEFINED_FUNCTION),
						 errmsg("could not identify an equality operator for type %s",
								format_type_be(fc_element_type))));
			fcinfo->flinfo->fn_extra = (void *) fc_typentry;
		}
		fc_typlen = fc_typentry->typlen;
		fc_typbyval = fc_typentry->typbyval;
		fc_typalign = fc_typentry->typalign;

		/*
		 * 将运算符应用于每对数组元素。
		 */
		InitFunctionCallInfoData(*fc_locfcinfo, &fc_typentry->eq_opr_finfo, 2,
								 fc_collation, NULL, NULL);

		/* 循环遍历源数据 */
		fc_nitems = ArrayGetNItems(fc_ndims1, fc_dims1);
		array_iter_setup(&fc_it1, fc_array1);
		array_iter_setup(&fc_it2, fc_array2);

		for (fc_i = 0; fc_i < fc_nitems; fc_i++)
		{
			Datum		fc_elt1;
			Datum		fc_elt2;
			bool		fc_isnull1;
			bool		fc_isnull2;
			bool		fc_oprresult;

			/* 获取元素，检查是否为 NULL */
			fc_elt1 = array_iter_next(&fc_it1, &fc_isnull1, fc_i,
								   fc_typlen, fc_typbyval, fc_typalign);
			fc_elt2 = array_iter_next(&fc_it2, &fc_isnull2, fc_i,
								   fc_typlen, fc_typbyval, fc_typalign);

			/*
			 * 我们认为两个 NULL 相等；NULL 和非 NULL 不相等。
			 */
			if (fc_isnull1 && fc_isnull2)
				continue;
			if (fc_isnull1 || fc_isnull2)
			{
				fc_result = false;
				break;
			}

			/*
			 * 将运算符应用于元素对；将 NULL 视为 false
			 */
			fc_locfcinfo->args[0].value = fc_elt1;
			fc_locfcinfo->args[0].isnull = false;
			fc_locfcinfo->args[1].value = fc_elt2;
			fc_locfcinfo->args[1].isnull = false;
			fc_locfcinfo->isnull = false;
			fc_oprresult = DatumGetBool(FunctionCallInvoke(fc_locfcinfo));
			if (fc_locfcinfo->isnull || !fc_oprresult)
			{
				fc_result = false;
				break;
			}
		}
	}

	/* 避免在处理已烤制输入时泄漏内存。 */
	AARR_FREE_IF_COPY(fc_array1, 0);
	AARR_FREE_IF_COPY(fc_array2, 1);

	PG_RETURN_BOOL(fc_result);
}


/*-----------------------------------------------------------------------------
 * array-array 布尔运算符：
 *		给定两个数组，迭代比较运算符
 *		在该数组上。采用类似于文本比较
 *		函数的逻辑，除了元素逐一比较，
 *		而不是逐字符比较。
 *----------------------------------------------------------------------------
 */

Datum array_ne(PG_FUNCTION_ARGS)
{
	PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo)));
}

Datum array_lt(PG_FUNCTION_ARGS)
{
	PG_RETURN_BOOL(fc_array_cmp(fcinfo) < 0);
}

Datum array_gt(PG_FUNCTION_ARGS)
{
	PG_RETURN_BOOL(fc_array_cmp(fcinfo) > 0);
}

Datum array_le(PG_FUNCTION_ARGS)
{
	PG_RETURN_BOOL(fc_array_cmp(fcinfo) <= 0);
}

Datum array_ge(PG_FUNCTION_ARGS)
{
	PG_RETURN_BOOL(fc_array_cmp(fcinfo) >= 0);
}

Datum btarraycmp(PG_FUNCTION_ARGS)
{
	PG_RETURN_INT32(fc_array_cmp(fcinfo));
}

/*
 * array_cmp()
 * 数组的内部比较函数。
 *
 * 返回 -1，0 或 1
 */
static int fc_array_cmp(FunctionCallInfo fcinfo)
{
	LOCAL_FCINFO(fc_locfcinfo, 2);
	AnyArrayType *fc_array1 = PG_GETARG_ANY_ARRAY_P(0);
	AnyArrayType *fc_array2 = PG_GETARG_ANY_ARRAY_P(1);
	Oid			fc_collation = PG_GET_COLLATION();
	int			fc_ndims1 = AARR_NDIM(fc_array1);
	int			fc_ndims2 = AARR_NDIM(fc_array2);
	int		   *fc_dims1 = AARR_DIMS(fc_array1);
	int		   *fc_dims2 = AARR_DIMS(fc_array2);
	int			fc_nitems1 = ArrayGetNItems(fc_ndims1, fc_dims1);
	int			fc_nitems2 = ArrayGetNItems(fc_ndims2, fc_dims2);
	Oid			fc_element_type = AARR_ELEMTYPE(fc_array1);
	int			fc_result = 0;
	TypeCacheEntry *fc_typentry;
	int			fc_typlen;
	bool		fc_typbyval;
	char		fc_typalign;
	int			fc_min_nitems;
	array_iter	fc_it1;
	array_iter	fc_it2;
	int			fc_i;

	if (fc_element_type != AARR_ELEMTYPE(fc_array2))
		ereport(ERROR,
				(errcode(ERRCODE_DATATYPE_MISMATCH),
				 errmsg("cannot compare arrays of different element types")));

	/*
	 * 我们安排在每系列调用中仅查找一次比较函数，
	 * 假设元素类型在我们之下不变。使用 typcache，
	 * 以便在作为索引支持函数时没有内存泄漏。
	 */
	fc_typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
	if (fc_typentry == NULL ||
		fc_typentry->type_id != fc_element_type)
	{
		fc_typentry = lookup_type_cache(fc_element_type,
									 TYPECACHE_CMP_PROC_FINFO);
		if (!OidIsValid(fc_typentry->cmp_proc_finfo.fn_oid))
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_FUNCTION),
					 errmsg("could not identify a comparison function for type %s",
							format_type_be(fc_element_type))));
		fcinfo->flinfo->fn_extra = (void *) fc_typentry;
	}
	fc_typlen = fc_typentry->typlen;
	fc_typbyval = fc_typentry->typbyval;
	fc_typalign = fc_typentry->typalign;

	/*
	 * 将运算符应用于每对数组元素。
	 */
	InitFunctionCallInfoData(*fc_locfcinfo, &fc_typentry->cmp_proc_finfo, 2,
							 fc_collation, NULL, NULL);

	/* 循环遍历源数据 */
	fc_min_nitems = Min(fc_nitems1, fc_nitems2);
	array_iter_setup(&fc_it1, fc_array1);
	array_iter_setup(&fc_it2, fc_array2);

	for (fc_i = 0; fc_i < fc_min_nitems; fc_i++)
	{
		Datum		fc_elt1;
		Datum		fc_elt2;
		bool		fc_isnull1;
		bool		fc_isnull2;
		int32		fc_cmpresult;

		/* 获取元素，检查是否为 NULL */
		fc_elt1 = array_iter_next(&fc_it1, &fc_isnull1, fc_i, fc_typlen, fc_typbyval, fc_typalign);
		fc_elt2 = array_iter_next(&fc_it2, &fc_isnull2, fc_i, fc_typlen, fc_typbyval, fc_typalign);

		/*
		 * 我们认为两个 NULL 相等；NULL > 非 NULL。
		 */
		if (fc_isnull1 && fc_isnull2)
			continue;
		if (fc_isnull1)
		{
			/* arg1 大于 arg2 */
			fc_result = 1;
			break;
		}
		if (fc_isnull2)
		{
			/* arg1 小于 arg2 */
			fc_result = -1;
			break;
		}

		/* 比较元素对 */
		fc_locfcinfo->args[0].value = fc_elt1;
		fc_locfcinfo->args[0].isnull = false;
		fc_locfcinfo->args[1].value = fc_elt2;
		fc_locfcinfo->args[1].isnull = false;
		fc_cmpresult = DatumGetInt32(FunctionCallInvoke(fc_locfcinfo));

		/* 我们不期望比较支持函数返回 NULL */
		Assert(!fc_locfcinfo->isnull);

		if (fc_cmpresult == 0)
			continue;			/* 等于 */

		if (fc_cmpresult < 0)
		{
			/* arg1 小于 arg2 */
			fc_result = -1;
			break;
		}
		else
		{
			/* arg1 大于 arg2 */
			fc_result = 1;
			break;
		}
	}

	/*
	 * 如果数组包含相同数据（直到较短的一个结束），应用
	 * 附加规则按维度排序。不同信息位的相对重要性
	 * 是历史性的；主要是我们只关心
	 * 不会对不同维度的数组说“相等”。
	 */
	if (fc_result == 0)
	{
		if (fc_nitems1 != fc_nitems2)
			fc_result = (fc_nitems1 < fc_nitems2) ? -1 : 1;
		else if (fc_ndims1 != fc_ndims2)
			fc_result = (fc_ndims1 < fc_ndims2) ? -1 : 1;
		else
		{
			for (fc_i = 0; fc_i < fc_ndims1; fc_i++)
			{
				if (fc_dims1[fc_i] != fc_dims2[fc_i])
				{
					fc_result = (fc_dims1[fc_i] < fc_dims2[fc_i]) ? -1 : 1;
					break;
				}
			}
			if (fc_result == 0)
			{
				int		   *fc_lbound1 = AARR_LBOUND(fc_array1);
				int		   *fc_lbound2 = AARR_LBOUND(fc_array2);

				for (fc_i = 0; fc_i < fc_ndims1; fc_i++)
				{
					if (fc_lbound1[fc_i] != fc_lbound2[fc_i])
					{
						fc_result = (fc_lbound1[fc_i] < fc_lbound2[fc_i]) ? -1 : 1;
						break;
					}
				}
			}
		}
	}

	/* 避免在处理已烤制输入时泄漏内存。 */
	AARR_FREE_IF_COPY(fc_array1, 0);
	AARR_FREE_IF_COPY(fc_array2, 1);

	return fc_result;
}


/*-----------------------------------------------------------------------------
 * 数组哈希
 *		对元素进行哈希并组合结果。
 *----------------------------------------------------------------------------
 */

Datum hash_array(PG_FUNCTION_ARGS)
{
	LOCAL_FCINFO(fc_locfcinfo, 1);
	AnyArrayType *fc_array = PG_GETARG_ANY_ARRAY_P(0);
	int			fc_ndims = AARR_NDIM(fc_array);
	int		   *fc_dims = AARR_DIMS(fc_array);
	Oid			fc_element_type = AARR_ELEMTYPE(fc_array);
	uint32		fc_result = 1;
	int			fc_nitems;
	TypeCacheEntry *fc_typentry;
	int			fc_typlen;
	bool		fc_typbyval;
	char		fc_typalign;
	int			fc_i;
	array_iter	fc_iter;

	/*
	 * 我们安排在每系列调用中仅查找一次哈希函数，
	 * 假设元素类型在我们之下不变。使用 typcache
	 * 以便在作为索引支持函数时没有内存泄漏。
	 */
	fc_typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
	if (fc_typentry == NULL ||
		fc_typentry->type_id != fc_element_type)
	{
		fc_typentry = lookup_type_cache(fc_element_type,
									 TYPECACHE_HASH_PROC_FINFO);
		if (!OidIsValid(fc_typentry->hash_proc_finfo.fn_oid) && fc_element_type != RECORDOID)
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_FUNCTION),
					 errmsg("could not identify a hash function for type %s",
							format_type_be(fc_element_type))));

		/*
		 * 类型缓存不认为记录是可哈希的（见
		 * cache_record_field_properties()），但既然我们在这里，
		 * 我们就承诺进行哈希，因此可以假设它是可哈希的。
		 * 最坏的情况是，如果记录的任何组件不支持哈希，
		 * 我们会在执行时失败。
		 */
		if (fc_element_type == RECORDOID)
		{
			MemoryContext fc_oldcontext;
			TypeCacheEntry *fc_record_typentry;

			fc_oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);

			/*
			 * 创建假类型缓存条目结构。注意，我们不能直接
			 * 修改 typentry，因为这直接指向类型
			 * 缓存。
			 */
			fc_record_typentry = palloc0(sizeof(*fc_record_typentry));
			fc_record_typentry->type_id = fc_element_type;

			/* 填入我们需要的内容 */
			fc_record_typentry->typlen = fc_typentry->typlen;
			fc_record_typentry->typbyval = fc_typentry->typbyval;
			fc_record_typentry->typalign = fc_typentry->typalign;
			fmgr_info(F_HASH_RECORD, &fc_record_typentry->hash_proc_finfo);

			MemoryContextSwitchTo(fc_oldcontext);

			fc_typentry = fc_record_typentry;
		}

		fcinfo->flinfo->fn_extra = (void *) fc_typentry;
	}

	fc_typlen = fc_typentry->typlen;
	fc_typbyval = fc_typentry->typbyval;
	fc_typalign = fc_typentry->typalign;

	/*
	 * 对每个数组元素应用哈希函数。
	 */
	InitFunctionCallInfoData(*fc_locfcinfo, &fc_typentry->hash_proc_finfo, 1,
							 PG_GET_COLLATION(), NULL, NULL);

	/* 循环遍历源数据 */
	fc_nitems = ArrayGetNItems(fc_ndims, fc_dims);
	array_iter_setup(&fc_iter, fc_array);

	for (fc_i = 0; fc_i < fc_nitems; fc_i++)
	{
		Datum		fc_elt;
		bool		fc_isnull;
		uint32		fc_elthash;

		/* 获取元素，检查是否为NULL */
		fc_elt = array_iter_next(&fc_iter, &fc_isnull, fc_i, fc_typlen, fc_typbyval, fc_typalign);

		if (fc_isnull)
		{
			/* 将空值视为哈希值为0 */
			fc_elthash = 0;
		}
		else
		{
			/* 应用哈希函数 */
			fc_locfcinfo->args[0].value = fc_elt;
			fc_locfcinfo->args[0].isnull = false;
			fc_elthash = DatumGetUInt32(FunctionCallInvoke(fc_locfcinfo));
			/* 我们不期望哈希函数返回空值 */
			Assert(!fc_locfcinfo->isnull);
		}

		/*
		 * 通过将当前值乘以31并加上新元素的哈希值来组合连续元素的哈希值。
		 *
		 * 结果是一个和，其中每个元素的哈希值乘以不同的31的幂。 这是模2^32
		 * 算术，而31的幂模2^32形成一个阶数为2^27的循环群。所以对于最多2^27个元素的数组，每个元素的
		 * 哈希值乘以不同的（奇数）数，从而实现所有元素的哈希值的良好混合。
		 */
		fc_result = (fc_result << 5) - fc_result + fc_elthash;
	}

	/* 避免在处理已烤制输入时泄漏内存。 */
	AARR_FREE_IF_COPY(fc_array, 0);

	PG_RETURN_UINT32(fc_result);
}

/*
 * 通过将值哈希到64位值来返回64位值，带有种子。
 * 否则，类似于hash_array。
 */
Datum hash_array_extended(PG_FUNCTION_ARGS)
{
	LOCAL_FCINFO(fc_locfcinfo, 2);
	AnyArrayType *fc_array = PG_GETARG_ANY_ARRAY_P(0);
	uint64		fc_seed = PG_GETARG_INT64(1);
	int			fc_ndims = AARR_NDIM(fc_array);
	int		   *fc_dims = AARR_DIMS(fc_array);
	Oid			fc_element_type = AARR_ELEMTYPE(fc_array);
	uint64		fc_result = 1;
	int			fc_nitems;
	TypeCacheEntry *fc_typentry;
	int			fc_typlen;
	bool		fc_typbyval;
	char		fc_typalign;
	int			fc_i;
	array_iter	fc_iter;

	fc_typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
	if (fc_typentry == NULL ||
		fc_typentry->type_id != fc_element_type)
	{
		fc_typentry = lookup_type_cache(fc_element_type,
									 TYPECACHE_HASH_EXTENDED_PROC_FINFO);
		if (!OidIsValid(fc_typentry->hash_extended_proc_finfo.fn_oid))
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_FUNCTION),
					 errmsg("could not identify an extended hash function for type %s",
							format_type_be(fc_element_type))));
		fcinfo->flinfo->fn_extra = (void *) fc_typentry;
	}
	fc_typlen = fc_typentry->typlen;
	fc_typbyval = fc_typentry->typbyval;
	fc_typalign = fc_typentry->typalign;

	InitFunctionCallInfoData(*fc_locfcinfo, &fc_typentry->hash_extended_proc_finfo, 2,
							 PG_GET_COLLATION(), NULL, NULL);

	/* 循环遍历源数据 */
	fc_nitems = ArrayGetNItems(fc_ndims, fc_dims);
	array_iter_setup(&fc_iter, fc_array);

	for (fc_i = 0; fc_i < fc_nitems; fc_i++)
	{
		Datum		fc_elt;
		bool		fc_isnull;
		uint64		fc_elthash;

		/* 获取元素，检查是否为NULL */
		fc_elt = array_iter_next(&fc_iter, &fc_isnull, fc_i, fc_typlen, fc_typbyval, fc_typalign);

		if (fc_isnull)
		{
			fc_elthash = 0;
		}
		else
		{
			/* 应用哈希函数 */
			fc_locfcinfo->args[0].value = fc_elt;
			fc_locfcinfo->args[0].isnull = false;
			fc_locfcinfo->args[1].value = Int64GetDatum(fc_seed);
			fc_locfcinfo->args[1].isnull = false;
			fc_elthash = DatumGetUInt64(FunctionCallInvoke(fc_locfcinfo));
			/* 我们不期望哈希函数返回空值 */
			Assert(!fc_locfcinfo->isnull);
		}

		fc_result = (fc_result << 5) - fc_result + fc_elthash;
	}

	AARR_FREE_IF_COPY(fc_array, 0);

	PG_RETURN_UINT64(fc_result);
}


/* -----------------------------------------------------------------------------
 * 数组重叠/包含比较
 *		这些使用与array_eq相同的方法来比较数组元素。
 *		我们只考虑数组的元素，忽略维度。
 * ----------------------------------------------------------------------------
 */

/*
 * array_contain_compare :
 *		  比较两个数组的重叠/包含
 *
 * 当matchall为真时，如果array1的所有成员都在array2中，则返回真。
 * 当matchall为假时，如果array1的任何成员在array2中，则返回真。
 */
static bool fc_array_contain_compare(AnyArrayType *fc_array1, AnyArrayType *fc_array2, Oid fc_collation,
					  bool fc_matchall, void **fc_fn_extra)
{
	LOCAL_FCINFO(fc_locfcinfo, 2);
	bool		fc_result = fc_matchall;
	Oid			fc_element_type = AARR_ELEMTYPE(fc_array1);
	TypeCacheEntry *fc_typentry;
	int			fc_nelems1;
	Datum	   *fc_values2;
	bool	   *fc_nulls2;
	int			fc_nelems2;
	int			fc_typlen;
	bool		fc_typbyval;
	char		fc_typalign;
	int			fc_i;
	int			fc_j;
	array_iter	fc_it1;

	if (fc_element_type != AARR_ELEMTYPE(fc_array2))
		ereport(ERROR,
				(errcode(ERRCODE_DATATYPE_MISMATCH),
				 errmsg("cannot compare arrays of different element types")));

	/*
	 * 我们安排只在一系列调用中查找相等函数一次，假设元素类型在我们下面不变。
	 * typcache用于确保在用作索引支持函数时没有内存泄漏。
	 */
	fc_typentry = (TypeCacheEntry *) *fc_fn_extra;
	if (fc_typentry == NULL ||
		fc_typentry->type_id != fc_element_type)
	{
		fc_typentry = lookup_type_cache(fc_element_type,
									 TYPECACHE_EQ_OPR_FINFO);
		if (!OidIsValid(fc_typentry->eq_opr_finfo.fn_oid))
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_FUNCTION),
					 errmsg("could not identify an equality operator for type %s",
							format_type_be(fc_element_type))));
		*fc_fn_extra = (void *) fc_typentry;
	}
	fc_typlen = fc_typentry->typlen;
	fc_typbyval = fc_typentry->typbyval;
	fc_typalign = fc_typentry->typalign;

	/*
	 * 由于我们可能需要多次扫描array2，因此值得在其上使用deconstruct_array。
	 * 然而，由于我们很可能不需要查看所有内容，我们以艰难的方式扫描array1。
	 */
	if (VARATT_IS_EXPANDED_HEADER(fc_array2))
	{
		/* 即使输入是只读的，这也应该是安全的 */
		deconstruct_expanded_array(&(fc_array2->xpn));
		fc_values2 = fc_array2->xpn.dvalues;
		fc_nulls2 = fc_array2->xpn.dnulls;
		fc_nelems2 = fc_array2->xpn.nelems;
	}
	else
		deconstruct_array((ArrayType *) fc_array2,
						  fc_element_type, fc_typlen, fc_typbyval, fc_typalign,
						  &fc_values2, &fc_nulls2, &fc_nelems2);

	/*
	 * 将比较运算符应用于每对数组元素。
	 */
	InitFunctionCallInfoData(*fc_locfcinfo, &fc_typentry->eq_opr_finfo, 2,
							 fc_collation, NULL, NULL);

	/* 循环遍历源数据 */
	fc_nelems1 = ArrayGetNItems(AARR_NDIM(fc_array1), AARR_DIMS(fc_array1));
	array_iter_setup(&fc_it1, fc_array1);

	for (fc_i = 0; fc_i < fc_nelems1; fc_i++)
	{
		Datum		fc_elt1;
		bool		fc_isnull1;

		/* 获取元素，检查是否为NULL */
		fc_elt1 = array_iter_next(&fc_it1, &fc_isnull1, fc_i, fc_typlen, fc_typbyval, fc_typalign);

		/*
		 * 我们假设比较运算符是严格的，因此NULL不能匹配任何东西。
		 * XXX这与array_eq的“NULL=NULL”行为不同，我们应该这样做吗？
		 */
		if (fc_isnull1)
		{
			if (fc_matchall)
			{
				fc_result = false;
				break;
			}
			continue;
		}

		for (fc_j = 0; fc_j < fc_nelems2; fc_j++)
		{
			Datum		fc_elt2 = fc_values2[fc_j];
			bool		fc_isnull2 = fc_nulls2 ? fc_nulls2[fc_j] : false;
			bool		fc_oprresult;

			if (fc_isnull2)
				continue;		/* 不能匹配 */

			/*
			 * 将运算符应用于元素对；将 NULL 视为 false
			 */
			fc_locfcinfo->args[0].value = fc_elt1;
			fc_locfcinfo->args[0].isnull = false;
			fc_locfcinfo->args[1].value = fc_elt2;
			fc_locfcinfo->args[1].isnull = false;
			fc_locfcinfo->isnull = false;
			fc_oprresult = DatumGetBool(FunctionCallInvoke(fc_locfcinfo));
			if (!fc_locfcinfo->isnull && fc_oprresult)
				break;
		}

		if (fc_j < fc_nelems2)
		{
			/* 找到elt1的匹配 */
			if (!fc_matchall)
			{
				fc_result = true;
				break;
			}
		}
		else
		{
			/* elt1没有匹配 */
			if (fc_matchall)
			{
				fc_result = false;
				break;
			}
		}
	}

	return fc_result;
}

Datum arrayoverlap(PG_FUNCTION_ARGS)
{
	AnyArrayType *fc_array1 = PG_GETARG_ANY_ARRAY_P(0);
	AnyArrayType *fc_array2 = PG_GETARG_ANY_ARRAY_P(1);
	Oid			fc_collation = PG_GET_COLLATION();
	bool		fc_result;

	fc_result = fc_array_contain_compare(fc_array1, fc_array2, fc_collation, false,
								   &fcinfo->flinfo->fn_extra);

	/* 避免在处理已烤制输入时泄漏内存。 */
	AARR_FREE_IF_COPY(fc_array1, 0);
	AARR_FREE_IF_COPY(fc_array2, 1);

	PG_RETURN_BOOL(fc_result);
}

Datum arraycontains(PG_FUNCTION_ARGS)
{
	AnyArrayType *fc_array1 = PG_GETARG_ANY_ARRAY_P(0);
	AnyArrayType *fc_array2 = PG_GETARG_ANY_ARRAY_P(1);
	Oid			fc_collation = PG_GET_COLLATION();
	bool		fc_result;

	fc_result = fc_array_contain_compare(fc_array2, fc_array1, fc_collation, true,
								   &fcinfo->flinfo->fn_extra);

	/* 避免在处理已烤制输入时泄漏内存。 */
	AARR_FREE_IF_COPY(fc_array1, 0);
	AARR_FREE_IF_COPY(fc_array2, 1);

	PG_RETURN_BOOL(fc_result);
}

Datum arraycontained(PG_FUNCTION_ARGS)
{
	AnyArrayType *fc_array1 = PG_GETARG_ANY_ARRAY_P(0);
	AnyArrayType *fc_array2 = PG_GETARG_ANY_ARRAY_P(1);
	Oid			fc_collation = PG_GET_COLLATION();
	bool		fc_result;

	fc_result = fc_array_contain_compare(fc_array1, fc_array2, fc_collation, true,
								   &fcinfo->flinfo->fn_extra);

	/* 避免在处理已烤制输入时泄漏内存。 */
	AARR_FREE_IF_COPY(fc_array1, 0);
	AARR_FREE_IF_COPY(fc_array2, 1);

	PG_RETURN_BOOL(fc_result);
}


/* -----------------------------------------------------------------------------
 * 数组迭代函数
 *		这些函数用于高效地迭代数组
 * -----------------------------------------------------------------------------
 */

/*
 * array_create_iterator --- 设置为迭代数组
 *
 * 如果slice_ndim为零，我们将逐元素迭代；返回的
 * 数据项为数组的元素类型。
 *
 * 如果slice_ndim为1..ARR_NDIM(arr)，我们将按切片迭代：
 * 返回的数据项与'arr'的数组类型相同，但大小
 * 等于'arr'的最右侧N维。
 *
 * 传入的数组必须在迭代器的整个生命周期内保持有效。
 */
ArrayIterator
array_create_iterator(ArrayType *fc_arr, int fc_slice_ndim, ArrayMetaState *fc_mstate)
{
	ArrayIterator fc_iterator = palloc0(sizeof(ArrayIteratorData));

	/*
	 * 对输入进行合理性检查 --- 调用者应该已经正确提供了这些
	 */
	Assert(PointerIsValid(fc_arr));
	if (fc_slice_ndim < 0 || fc_slice_ndim > ARR_NDIM(fc_arr))
		elog(ERROR, "invalid arguments to array_create_iterator");

	/*
	 * 记住数组及其元素类型的基本信息
	 */
	fc_iterator->arr = fc_arr;
	fc_iterator->nullbitmap = ARR_NULLBITMAP(fc_arr);
	fc_iterator->nitems = ArrayGetNItems(ARR_NDIM(fc_arr), ARR_DIMS(fc_arr));

	if (fc_mstate != NULL)
	{
		Assert(fc_mstate->element_type == ARR_ELEMTYPE(fc_arr));

		fc_iterator->typlen = fc_mstate->typlen;
		fc_iterator->typbyval = fc_mstate->typbyval;
		fc_iterator->typalign = fc_mstate->typalign;
	}
	else
		get_typlenbyvalalign(ARR_ELEMTYPE(fc_arr),
							 &fc_iterator->typlen,
							 &fc_iterator->typbyval,
							 &fc_iterator->typalign);

	/*
	 * 记住切片参数。
	 */
	fc_iterator->slice_ndim = fc_slice_ndim;

	if (fc_slice_ndim > 0)
	{
		/*
		 * 获取指向数组的dims和lbound数组的指针，以表示
		 * 切片的dims/lbound数组。这些与数组的
		 * 最右侧N维相同。
		 */
		fc_iterator->slice_dims = ARR_DIMS(fc_arr) + ARR_NDIM(fc_arr) - fc_slice_ndim;
		fc_iterator->slice_lbound = ARR_LBOUND(fc_arr) + ARR_NDIM(fc_arr) - fc_slice_ndim;

		/*
		 * 计算切片中的元素数量。
		 */
		fc_iterator->slice_len = ArrayGetNItems(fc_slice_ndim,
											 fc_iterator->slice_dims);

		/*
		 * 创建用于构建子数组的工作区。
		 */
		fc_iterator->slice_values = (Datum *)
			palloc(fc_iterator->slice_len * sizeof(Datum));
		fc_iterator->slice_nulls = (bool *)
			palloc(fc_iterator->slice_len * sizeof(bool));
	}

	/*
	 * 初始化我们的数据指针和线性元素编号。这些将在
	 * array_iterate()期间通过数组前进。
	 */
	fc_iterator->data_ptr = ARR_DATA_PTR(fc_arr);
	fc_iterator->current_item = 0;

	return fc_iterator;
}

/*
 * 迭代通过'iterator'引用的数组。
 *
 * 只要还有另一个元素（或切片），就将其返回到
 * *value / *isnull，并返回真。当没有更多数据时返回假。
 */
bool array_iterate(ArrayIterator fc_iterator, Datum *fc_value, bool *fc_isnull)
{
	/* 如果我们到达数组的末尾，就完成了 */
	if (fc_iterator->current_item >= fc_iterator->nitems)
		return false;

	if (fc_iterator->slice_ndim == 0)
	{
		/*
		 * 标量情况：返回一个元素。
		 */
		if (fc_array_get_isnull(fc_iterator->nullbitmap, fc_iterator->current_item++))
		{
			*fc_isnull = true;
			*fc_value = (Datum) 0;
		}
		else
		{
			/* 非空，因此获取单个Datum以返回 */
			char	   *fc_p = fc_iterator->data_ptr;

			*fc_isnull = false;
			*fc_value = fetch_att(fc_p, fc_iterator->typbyval, fc_iterator->typlen);

			/* 将我们的数据指针向前移动到下一个元素 */
			fc_p = att_addlength_pointer(fc_p, fc_iterator->typlen, fc_p);
			fc_p = (char *) att_align_nominal(fc_p, fc_iterator->typalign);
			fc_iterator->data_ptr = fc_p;
		}
	}
	else
	{
		/*
		 * 切片案例：构建并返回请求大小的数组。
		 */
		ArrayType  *fc_result;
		Datum	   *fc_values = fc_iterator->slice_values;
		bool	   *fc_nulls = fc_iterator->slice_nulls;
		char	   *fc_p = fc_iterator->data_ptr;
		int			fc_i;

		for (fc_i = 0; fc_i < fc_iterator->slice_len; fc_i++)
		{
			if (fc_array_get_isnull(fc_iterator->nullbitmap,
								 fc_iterator->current_item++))
			{
				fc_nulls[fc_i] = true;
				fc_values[fc_i] = (Datum) 0;
			}
			else
			{
				fc_nulls[fc_i] = false;
				fc_values[fc_i] = fetch_att(fc_p, fc_iterator->typbyval, fc_iterator->typlen);

				/* 将我们的数据指针向前移动到下一个元素 */
				fc_p = att_addlength_pointer(fc_p, fc_iterator->typlen, fc_p);
				fc_p = (char *) att_align_nominal(fc_p, fc_iterator->typalign);
			}
		}

		fc_iterator->data_ptr = fc_p;

		fc_result = construct_md_array(fc_values,
									fc_nulls,
									fc_iterator->slice_ndim,
									fc_iterator->slice_dims,
									fc_iterator->slice_lbound,
									ARR_ELEMTYPE(fc_iterator->arr),
									fc_iterator->typlen,
									fc_iterator->typbyval,
									fc_iterator->typalign);

		*fc_isnull = false;
		*fc_value = PointerGetDatum(fc_result);
	}

	return true;
}

/*
 * 释放一个 ArrayIterator 数据结构
 */
void array_free_iterator(ArrayIterator fc_iterator)
{
	if (fc_iterator->slice_ndim > 0)
	{
		pfree(fc_iterator->slice_values);
		pfree(fc_iterator->slice_nulls);
	}
	pfree(fc_iterator);
}


/***************************************************************************/
/******************|		  支持例程			  |*****************/
/***************************************************************************/

/*
 * 检查特定数组元素是否为 NULL
 *
 * nullbitmap：指向数组的 null 位图的指针（如果没有则为 NULL）
 * offset：数组元素的基于 0 的线性元素编号
 */
static bool fc_array_get_isnull(const bits8 *fc_nullbitmap, int fc_offset)
{
	if (fc_nullbitmap == NULL)
		return false;			/* 假定不是 null */
	if (fc_nullbitmap[fc_offset / 8] & (1 << (fc_offset % 8)))
		return false;			/* not null */
	return true;
}

/*
 * 设置特定数组元素的 null 位图条目
 *
 * nullbitmap：指向数组的 null 位图的指针（不能为 NULL）
 * offset：数组元素的基于 0 的线性元素编号
 * isNull：要设置的 null 状态
 */
static void fc_array_set_isnull(bits8 *fc_nullbitmap, int fc_offset, bool fc_isNull)
{
	int			fc_bitmask;

	fc_nullbitmap += fc_offset / 8;
	fc_bitmask = 1 << (fc_offset % 8);
	if (fc_isNull)
		*fc_nullbitmap &= ~fc_bitmask;
	else
		*fc_nullbitmap |= fc_bitmask;
}

/*
 * 获取指针处的数组元素，正确转换为 Datum
 *
 * 调用者必须处理 NULL 元素的情况
 */
static Datum fc_ArrayCast(char *fc_value, bool fc_byval, int fc_len)
{
	return fetch_att(fc_value, fc_byval, fc_len);
}

/*
 * 将 datum 复制到 *dest 并返回使用的总空间（包括对齐填充）
 *
 * 调用者必须处理 NULL 元素的情况
 */
static int fc_ArrayCastAndSet(Datum fc_src,
				int fc_typlen,
				bool fc_typbyval,
				char fc_typalign,
				char *fc_dest)
{
	int			fc_inc;

	if (fc_typlen > 0)
	{
		if (fc_typbyval)
			store_att_byval(fc_dest, fc_src, fc_typlen);
		else
			memmove(fc_dest, DatumGetPointer(fc_src), fc_typlen);
		fc_inc = att_align_nominal(fc_typlen, fc_typalign);
	}
	else
	{
		Assert(!fc_typbyval);
		fc_inc = att_addlength_datum(0, fc_typlen, fc_src);
		memmove(fc_dest, DatumGetPointer(fc_src), fc_inc);
		fc_inc = att_align_nominal(fc_inc, fc_typalign);
	}

	return fc_inc;
}

/*
 * 将指针前移 nitems 个数组元素
 *
 * ptr：数组中的起始位置
 * offset：第一个元素的基于 0 的线性元素编号（*ptr 处的元素）
 * nullbitmap：数组的 null 位图的起始位置，如果没有则为 NULL
 * nitems：要前移的数组元素数量（>= 0）
 * typlen, typbyval, typalign：数组元素数据类型的存储参数
 *
 * 确保 nitems 在范围内是调用者的责任
 */
static char * fc_array_seek(char *fc_ptr, int fc_offset, bits8 *fc_nullbitmap, int fc_nitems,
		   int fc_typlen, bool fc_typbyval, char fc_typalign)
{
	int			fc_bitmask;
	int			fc_i;

	/* 如果是固定大小元素且没有 NULLs 就很简单 */
	if (fc_typlen > 0 && !fc_nullbitmap)
		return fc_ptr + fc_nitems * ((Size) att_align_nominal(fc_typlen, fc_typalign));

	/* 为 NULL 和非 NULL 的情况分别处理循环似乎是值得的 */
	if (fc_nullbitmap)
	{
		fc_nullbitmap += fc_offset / 8;
		fc_bitmask = 1 << (fc_offset % 8);

		for (fc_i = 0; fc_i < fc_nitems; fc_i++)
		{
			if (*fc_nullbitmap & fc_bitmask)
			{
				fc_ptr = att_addlength_pointer(fc_ptr, fc_typlen, fc_ptr);
				fc_ptr = (char *) att_align_nominal(fc_ptr, fc_typalign);
			}
			fc_bitmask <<= 1;
			if (fc_bitmask == 0x100)
			{
				fc_nullbitmap++;
				fc_bitmask = 1;
			}
		}
	}
	else
	{
		for (fc_i = 0; fc_i < fc_nitems; fc_i++)
		{
			fc_ptr = att_addlength_pointer(fc_ptr, fc_typlen, fc_ptr);
			fc_ptr = (char *) att_align_nominal(fc_ptr, fc_typalign);
		}
	}
	return fc_ptr;
}

/*
 * 计算从 *ptr 开始的 nitems 数组元素的总大小
 *
 * 参数与 array_seek 相同
 */
static int fc_array_nelems_size(char *fc_ptr, int fc_offset, bits8 *fc_nullbitmap, int fc_nitems,
				  int fc_typlen, bool fc_typbyval, char fc_typalign)
{
	return fc_array_seek(fc_ptr, fc_offset, fc_nullbitmap, fc_nitems,
					  fc_typlen, fc_typbyval, fc_typalign) - fc_ptr;
}

/*
 * 将 nitems 数组元素从 srcptr 复制到 destptr
 *
 * destptr：起始目标位置（必须有足够的空间！）
 * nitems：要复制的数组元素数量（>= 0）
 * srcptr：源数组中的起始位置
 * offset：第一个元素的基于 0 的线性元素编号（*srcptr 处的元素）
 * nullbitmap：源数组的 null 位图的起始位置，如果没有则为 NULL
 * typlen, typbyval, typalign：数组元素数据类型的存储参数
 *
 * 返回复制的字节数
 *
 * 注意：这并不处理设置目标的 null 位图！
 */
static int fc_array_copy(char *fc_destptr, int fc_nitems,
		   char *fc_srcptr, int fc_offset, bits8 *fc_nullbitmap,
		   int fc_typlen, bool fc_typbyval, char fc_typalign)
{
	int			fc_numbytes;

	fc_numbytes = fc_array_nelems_size(fc_srcptr, fc_offset, fc_nullbitmap, fc_nitems,
								 fc_typlen, fc_typbyval, fc_typalign);
	memcpy(fc_destptr, fc_srcptr, fc_numbytes);
	return fc_numbytes;
}

/*
 * 从源复制 nitems null 位图位到目标
 *
 * destbitmap：目标数组的 null 位图的起始位置（不能为 NULL）
 * destoffset：第一个目标元素的基于 0 的线性元素编号
 * srcbitmap：源数组的 null 位图的起始位置，如果没有则为 NULL
 * srcoffset：第一个源元素的基于 0 的线性元素编号
 * nitems：要复制的位数（>= 0）
 *
 * 如果 srcbitmap 为 NULL，则假定源全为非 NULL，并
 * 向目标位图填充 1。注意，仅更改目标映射中指定的位，其他位不变。
 *
 * 注意：这肯定可以使用标准的 bitblt 方法进行优化。
 * 然而，典型的 Postgres 数组是否有足够的元素使其值得担心并不清楚。此时，保持简单。
 */
void array_bitmap_copy(bits8 *fc_destbitmap, int fc_destoffset,
				  const bits8 *fc_srcbitmap, int fc_srcoffset,
				  int fc_nitems)
{
	int			fc_destbitmask,
				fc_destbitval,
				fc_srcbitmask,
				fc_srcbitval;

	Assert(fc_destbitmap);
	if (fc_nitems <= 0)
		return;					/* 不要冒险超出内存的末尾 */
	fc_destbitmap += fc_destoffset / 8;
	fc_destbitmask = 1 << (fc_destoffset % 8);
	fc_destbitval = *fc_destbitmap;
	if (fc_srcbitmap)
	{
		fc_srcbitmap += fc_srcoffset / 8;
		fc_srcbitmask = 1 << (fc_srcoffset % 8);
		fc_srcbitval = *fc_srcbitmap;
		while (fc_nitems-- > 0)
		{
			if (fc_srcbitval & fc_srcbitmask)
				fc_destbitval |= fc_destbitmask;
			else
				fc_destbitval &= ~fc_destbitmask;
			fc_destbitmask <<= 1;
			if (fc_destbitmask == 0x100)
			{
				*fc_destbitmap++ = fc_destbitval;
				fc_destbitmask = 1;
				if (fc_nitems > 0)
					fc_destbitval = *fc_destbitmap;
			}
			fc_srcbitmask <<= 1;
			if (fc_srcbitmask == 0x100)
			{
				fc_srcbitmap++;
				fc_srcbitmask = 1;
				if (fc_nitems > 0)
					fc_srcbitval = *fc_srcbitmap;
			}
		}
		if (fc_destbitmask != 1)
			*fc_destbitmap = fc_destbitval;
	}
	else
	{
		while (fc_nitems-- > 0)
		{
			fc_destbitval |= fc_destbitmask;
			fc_destbitmask <<= 1;
			if (fc_destbitmask == 0x100)
			{
				*fc_destbitmap++ = fc_destbitval;
				fc_destbitmask = 1;
				if (fc_nitems > 0)
					fc_destbitval = *fc_destbitmap;
			}
		}
		if (fc_destbitmask != 1)
			*fc_destbitmap = fc_destbitval;
	}
}

/*
 * 计算数组切片所需的空间
 *
 * 我们假定调用者已验证切片坐标是有效的。
 */
static int fc_array_slice_size(char *fc_arraydataptr, bits8 *fc_arraynullsptr,
				 int fc_ndim, int *fc_dim, int *fc_lb,
				 int *fc_st, int *fc_endp,
				 int fc_typlen, bool fc_typbyval, char fc_typalign)
{
	int			fc_src_offset,
				fc_span[MAXDIM],
				fc_prod[MAXDIM],
				fc_dist[MAXDIM],
				fc_indx[MAXDIM];
	char	   *fc_ptr;
	int			fc_i,
				fc_j,
				fc_inc;
	int			fc_count = 0;

	mda_get_range(fc_ndim, fc_span, fc_st, fc_endp);

	/* 对于没有 null 的固定元素长度来说非常简单... */
	if (fc_typlen > 0 && !fc_arraynullsptr)
		return ArrayGetNItems(fc_ndim, fc_span) * att_align_nominal(fc_typlen, fc_typalign);

	/* 否则就得按难的办法处理 */
	fc_src_offset = ArrayGetOffset(fc_ndim, fc_dim, fc_lb, fc_st);
	fc_ptr = fc_array_seek(fc_arraydataptr, 0, fc_arraynullsptr, fc_src_offset,
					 fc_typlen, fc_typbyval, fc_typalign);
	mda_get_prod(fc_ndim, fc_dim, fc_prod);
	mda_get_offset_values(fc_ndim, fc_dist, fc_prod, fc_span);
	for (fc_i = 0; fc_i < fc_ndim; fc_i++)
		fc_indx[fc_i] = 0;
	fc_j = fc_ndim - 1;
	do
	{
		if (fc_dist[fc_j])
		{
			fc_ptr = fc_array_seek(fc_ptr, fc_src_offset, fc_arraynullsptr, fc_dist[fc_j],
							 fc_typlen, fc_typbyval, fc_typalign);
			fc_src_offset += fc_dist[fc_j];
		}
		if (!fc_array_get_isnull(fc_arraynullsptr, fc_src_offset))
		{
			fc_inc = att_addlength_pointer(0, fc_typlen, fc_ptr);
			fc_inc = att_align_nominal(fc_inc, fc_typalign);
			fc_ptr += fc_inc;
			fc_count += fc_inc;
		}
		fc_src_offset++;
	} while ((fc_j = mda_next_tuple(fc_ndim, fc_indx, fc_span)) != -1);
	return fc_count;
}

/*
 * 将数组的切片提取到目标数组中的连续元素中。
 *
 * 我们假定调用者已验证切片坐标是有效的，
 * 为结果分配了足够的存储，并初始化了新数组的头部。
 */
static void fc_array_extract_slice(ArrayType *fc_newarray,
					int fc_ndim,
					int *fc_dim,
					int *fc_lb,
					char *fc_arraydataptr,
					bits8 *fc_arraynullsptr,
					int *fc_st,
					int *fc_endp,
					int fc_typlen,
					bool fc_typbyval,
					char fc_typalign)
{
	char	   *fc_destdataptr = ARR_DATA_PTR(fc_newarray);
	bits8	   *fc_destnullsptr = ARR_NULLBITMAP(fc_newarray);
	char	   *fc_srcdataptr;
	int			fc_src_offset,
				fc_dest_offset,
				fc_prod[MAXDIM],
				fc_span[MAXDIM],
				fc_dist[MAXDIM],
				fc_indx[MAXDIM];
	int			fc_i,
				fc_j,
				fc_inc;

	fc_src_offset = ArrayGetOffset(fc_ndim, fc_dim, fc_lb, fc_st);
	fc_srcdataptr = fc_array_seek(fc_arraydataptr, 0, fc_arraynullsptr, fc_src_offset,
							fc_typlen, fc_typbyval, fc_typalign);
	mda_get_prod(fc_ndim, fc_dim, fc_prod);
	mda_get_range(fc_ndim, fc_span, fc_st, fc_endp);
	mda_get_offset_values(fc_ndim, fc_dist, fc_prod, fc_span);
	for (fc_i = 0; fc_i < fc_ndim; fc_i++)
		fc_indx[fc_i] = 0;
	fc_dest_offset = 0;
	fc_j = fc_ndim - 1;
	do
	{
		if (fc_dist[fc_j])
		{
			/* 跳过不需要的元素 */
			fc_srcdataptr = fc_array_seek(fc_srcdataptr, fc_src_offset, fc_arraynullsptr,
									fc_dist[fc_j],
									fc_typlen, fc_typbyval, fc_typalign);
			fc_src_offset += fc_dist[fc_j];
		}
		fc_inc = fc_array_copy(fc_destdataptr, 1,
						 fc_srcdataptr, fc_src_offset, fc_arraynullsptr,
						 fc_typlen, fc_typbyval, fc_typalign);
		if (fc_destnullsptr)
			array_bitmap_copy(fc_destnullsptr, fc_dest_offset,
							  fc_arraynullsptr, fc_src_offset,
							  1);
		fc_destdataptr += fc_inc;
		fc_srcdataptr += fc_inc;
		fc_src_offset++;
		fc_dest_offset++;
	} while ((fc_j = mda_next_tuple(fc_ndim, fc_indx, fc_span)) != -1);
}

/*
 * 将切片插入到数组中。
 *
 * ndim/dim[]/lb[] 是原数组的维度。需要构造一个具有
 * 相同维度的新数组。destArray 必须已经分配并其头部已初始化。
 *
 * st[]/endp[] 标识要替换的切片。切片范围内的元素
 * 从连续元素的 srcArray 中获取；切片范围外的元素则从 origArray 复制。
 *
 * 我们假定调用者已验证切片坐标是有效的。
 */
static void fc_array_insert_slice(ArrayType *fc_destArray,
				   ArrayType *fc_origArray,
				   ArrayType *fc_srcArray,
				   int fc_ndim,
				   int *fc_dim,
				   int *fc_lb,
				   int *fc_st,
				   int *fc_endp,
				   int fc_typlen,
				   bool fc_typbyval,
				   char fc_typalign)
{
	char	   *fc_destPtr = ARR_DATA_PTR(fc_destArray);
	char	   *fc_origPtr = ARR_DATA_PTR(fc_origArray);
	char	   *fc_srcPtr = ARR_DATA_PTR(fc_srcArray);
	bits8	   *fc_destBitmap = ARR_NULLBITMAP(fc_destArray);
	bits8	   *fc_origBitmap = ARR_NULLBITMAP(fc_origArray);
	bits8	   *fc_srcBitmap = ARR_NULLBITMAP(fc_srcArray);
	int			fc_orignitems = ArrayGetNItems(ARR_NDIM(fc_origArray),
											ARR_DIMS(fc_origArray));
	int			fc_dest_offset,
				fc_orig_offset,
				fc_src_offset,
				fc_prod[MAXDIM],
				fc_span[MAXDIM],
				fc_dist[MAXDIM],
				fc_indx[MAXDIM];
	int			fc_i,
				fc_j,
				fc_inc;

	fc_dest_offset = ArrayGetOffset(fc_ndim, fc_dim, fc_lb, fc_st);
	/* 复制切片开始前的项目 */
	fc_inc = fc_array_copy(fc_destPtr, fc_dest_offset,
					 fc_origPtr, 0, fc_origBitmap,
					 fc_typlen, fc_typbyval, fc_typalign);
	fc_destPtr += fc_inc;
	fc_origPtr += fc_inc;
	if (fc_destBitmap)
		array_bitmap_copy(fc_destBitmap, 0, fc_origBitmap, 0, fc_dest_offset);
	fc_orig_offset = fc_dest_offset;
	mda_get_prod(fc_ndim, fc_dim, fc_prod);
	mda_get_range(fc_ndim, fc_span, fc_st, fc_endp);
	mda_get_offset_values(fc_ndim, fc_dist, fc_prod, fc_span);
	for (fc_i = 0; fc_i < fc_ndim; fc_i++)
		fc_indx[fc_i] = 0;
	fc_src_offset = 0;
	fc_j = fc_ndim - 1;
	do
	{
		/* 在这里和切片下一个部分之间复制/前进元素 */
		if (fc_dist[fc_j])
		{
			fc_inc = fc_array_copy(fc_destPtr, fc_dist[fc_j],
							 fc_origPtr, fc_orig_offset, fc_origBitmap,
							 fc_typlen, fc_typbyval, fc_typalign);
			fc_destPtr += fc_inc;
			fc_origPtr += fc_inc;
			if (fc_destBitmap)
				array_bitmap_copy(fc_destBitmap, fc_dest_offset,
								  fc_origBitmap, fc_orig_offset,
								  fc_dist[fc_j]);
			fc_dest_offset += fc_dist[fc_j];
			fc_orig_offset += fc_dist[fc_j];
		}
		/* 在此切片位置复制新元素 */
		fc_inc = fc_array_copy(fc_destPtr, 1,
						 fc_srcPtr, fc_src_offset, fc_srcBitmap,
						 fc_typlen, fc_typbyval, fc_typalign);
		if (fc_destBitmap)
			array_bitmap_copy(fc_destBitmap, fc_dest_offset,
							  fc_srcBitmap, fc_src_offset,
							  1);
		fc_destPtr += fc_inc;
		fc_srcPtr += fc_inc;
		fc_dest_offset++;
		fc_src_offset++;
		/* 在此切片位置前进旧元素 */
		fc_origPtr = fc_array_seek(fc_origPtr, fc_orig_offset, fc_origBitmap, 1,
							 fc_typlen, fc_typbyval, fc_typalign);
		fc_orig_offset++;
	} while ((fc_j = mda_next_tuple(fc_ndim, fc_indx, fc_span)) != -1);

	/* 不要遗漏末尾的任何数据 */
	fc_array_copy(fc_destPtr, fc_orignitems - fc_orig_offset,
			   fc_origPtr, fc_orig_offset, fc_origBitmap,
			   fc_typlen, fc_typbyval, fc_typalign);
	if (fc_destBitmap)
		array_bitmap_copy(fc_destBitmap, fc_dest_offset,
						  fc_origBitmap, fc_orig_offset,
						  fc_orignitems - fc_orig_offset);
}


/*
 * initArrayResult - 初始化一个空的 ArrayBuildState
 *
 *	element_type 是数组元素类型（必须是有效的数组元素类型）
 *	rcontext 是用于保持工作状态的地方
 *	subcontext 是一个标志，用于确定是否使用单独的内存上下文
 *
 * 注意：有两种常见的使用 accumArrayResult() 的方案。
 * 在较旧的方案中，您从一个 NULL ArrayBuildState 指针开始，
 * 并对每个元素调用一次 accumArrayResult。在此方案中，如果没有元素，
 * 您将得到一个 NULL 指针，您需要对此进行特殊处理。
 * 在较新的方案中，首先调用 initArrayResult，然后对每个元素调用一次 
 * accumArrayResult。在此方案中，您始终以一个非 NULL 指针结束，
 * 您可以将其传递给 makeArrayResult；如果没有元素，您将得到一个空数组。
 * 如果您想要的是空数组，这是首选的方案。
 *
 * 您可以选择是否为数组构建状态创建一个单独的内存上下文，
 * 或者是否直接在 rcontext 中分配它。
 *
 * 当有许多并发的小状态（例如，array_agg() 使用哈希聚合许多小组）时，
 * 为每个小状态使用单独的内存上下文可能导致严重的内存膨胀。
 * 在这种情况下，使用相同的内存上下文来初始化所有这些数组构建状态，并将
 * subcontext=false 传递给它。
 *
 * 在数组构建状态具有不同生命周期的情况下，使用单个内存上下文是不切实际的。
 * 相反，传递 subcontext=true，以便可以单独释放数组构建状态。
 */
ArrayBuildState *
initArrayResult(Oid fc_element_type, MemoryContext fc_rcontext, bool fc_subcontext)
{
	ArrayBuildState *fc_astate;
	MemoryContext fc_arr_context = fc_rcontext;

	/* 创建一个临时上下文来保存所有的垃圾 */
	if (fc_subcontext)
		fc_arr_context = AllocSetContextCreate(fc_rcontext,
											"accumArrayResult",
											ALLOCSET_DEFAULT_SIZES);

	fc_astate = (ArrayBuildState *)
		MemoryContextAlloc(fc_arr_context, sizeof(ArrayBuildState));
	fc_astate->mcontext = fc_arr_context;
	fc_astate->private_cxt = fc_subcontext;
	fc_astate->alen = (fc_subcontext ? 64 : 8);	/* 任意起始数组大小 */
	fc_astate->dvalues = (Datum *)
		MemoryContextAlloc(fc_arr_context, fc_astate->alen * sizeof(Datum));
	fc_astate->dnulls = (bool *)
		MemoryContextAlloc(fc_arr_context, fc_astate->alen * sizeof(bool));
	fc_astate->nelems = 0;
	fc_astate->element_type = fc_element_type;
	get_typlenbyvalalign(fc_element_type,
						 &fc_astate->typlen,
						 &fc_astate->typbyval,
						 &fc_astate->typalign);

	return fc_astate;
}

/*
 * accumArrayResult - 为数组结果累积一个（或更多）Datum
 *
 * astate 是工作状态（在第一次调用时可以为 NULL）
 * dvalue/disnull 代表要附加到数组的新 Datum
 * element_type 是 Datum 的类型（必须是有效的数组元素类型）
 * rcontext 是保存工作状态的地方
 */
ArrayBuildState *
accumArrayResult(ArrayBuildState *fc_astate,
				 Datum fc_dvalue, bool fc_disnull,
				 Oid fc_element_type,
				 MemoryContext fc_rcontext)
{
	MemoryContext fc_oldcontext;

	if (fc_astate == NULL)
	{
		/* 第一次 --- 初始化 */
		fc_astate = initArrayResult(fc_element_type, fc_rcontext, true);
	}
	else
	{
		Assert(fc_astate->element_type == fc_element_type);
	}

	fc_oldcontext = MemoryContextSwitchTo(fc_astate->mcontext);

	/* 如有需要，扩大 dvalues[]/dnulls[] */
	if (fc_astate->nelems >= fc_astate->alen)
	{
		fc_astate->alen *= 2;
		fc_astate->dvalues = (Datum *)
			repalloc(fc_astate->dvalues, fc_astate->alen * sizeof(Datum));
		fc_astate->dnulls = (bool *)
			repalloc(fc_astate->dnulls, fc_astate->alen * sizeof(bool));
	}

	/*
	 * 确保传递引用的数据被复制到 mcontext；如果是 varlena 也要进行解构。
	 * （你可能会认为在这里不需要解构，
	 * 因为 construct_md_array 可以稍后解构数组元素。
	 * 然而，我们不能让 construct_md_array 修改 ArrayBuildState
	 * 因为那将意味着 array_agg_finalfn 损坏了其输入，这是
	 * 禁止的。此外，这样做通常可以节省一步复制的步骤。）
	 */
	if (!fc_disnull && !fc_astate->typbyval)
	{
		if (fc_astate->typlen == -1)
			fc_dvalue = PointerGetDatum(PG_DETOAST_DATUM_COPY(fc_dvalue));
		else
			fc_dvalue = datumCopy(fc_dvalue, fc_astate->typbyval, fc_astate->typlen);
	}

	fc_astate->dvalues[fc_astate->nelems] = fc_dvalue;
	fc_astate->dnulls[fc_astate->nelems] = fc_disnull;
	fc_astate->nelems++;

	MemoryContextSwitchTo(fc_oldcontext);

	return fc_astate;
}

/*
 * makeArrayResult - 生成 accumArrayResult 的 1-D 最终结果
 *
 * 注意：仅在 astate 在一个单独的内存上下文中初始化时
 * 释放 astate（即在调用 initArrayResult 时使用 subcontext=true）。
 *
 * astate 是工作状态（不能为 NULL）
 * rcontext 是构造结果的地方
 */
Datum makeArrayResult(ArrayBuildState *fc_astate,
				MemoryContext fc_rcontext)
{
	int			fc_ndims;
	int			fc_dims[1];
	int			fc_lbs[1];

	/* 如果未提供元素，我们希望创建一个空数组 */
	fc_ndims = (fc_astate->nelems > 0) ? 1 : 0;
	fc_dims[0] = fc_astate->nelems;
	fc_lbs[0] = 1;

	return makeMdArrayResult(fc_astate, fc_ndims, fc_dims, fc_lbs, fc_rcontext,
							 fc_astate->private_cxt);
}

/*
 * makeMdArrayResult - 生成 accumArrayResult 的多维最终结果
 *
 * 小心：没有检查指定维度是否与累积的值数量相匹配。
 *
 * 注意：如果 astate 没有在单独的内存上下文中初始化
 * （也就是说，initArrayResult 使用 subcontext=false 被调用），
 * 则使用 release=true 是不合法的。相反，在适当时释放
 * astate 及其余上下文。
 *
 * astate 是工作状态（不能为 NULL）
 * rcontext 是构造结果的地方
 * release 是 true 如果可以释放工作状态
 */
Datum makeMdArrayResult(ArrayBuildState *fc_astate,
				  int fc_ndims,
				  int *fc_dims,
				  int *fc_lbs,
				  MemoryContext fc_rcontext,
				  bool fc_release)
{
	ArrayType  *fc_result;
	MemoryContext fc_oldcontext;

	/* 在 rcontext 中构建最终数组结果 */
	fc_oldcontext = MemoryContextSwitchTo(fc_rcontext);

	fc_result = construct_md_array(fc_astate->dvalues,
								fc_astate->dnulls,
								fc_ndims,
								fc_dims,
								fc_lbs,
								fc_astate->element_type,
								fc_astate->typlen,
								fc_astate->typbyval,
								fc_astate->typalign);

	MemoryContextSwitchTo(fc_oldcontext);

	/* 清理所有的垃圾 */
	if (fc_release)
	{
		Assert(fc_astate->private_cxt);
		MemoryContextDelete(fc_astate->mcontext);
	}

	return PointerGetDatum(fc_result);
}

/*
 * 以下三个函数提供与
 * initArrayResult/accumArrayResult/makeArrayResult 相同的 API，
 * 但它们接受的输入是数组，而不是数组元素，
 * 生成一个具有 N+1 维度的输出数组。输入必须具有
 * 相同的维度和元素类型。
 */

/*
 * initArrayResultArr - 初始化一个空的 ArrayBuildStateArr
 *
 * array_type 是数组类型（必须是有效的 varlena 数组类型）
 * element_type 是数组元素的类型（如果 InvalidOid 则查找）
 * rcontext 是保存工作状态的地方
 * subcontext 是一个标志，用于确定是否使用单独的内存上下文
 */
ArrayBuildStateArr *
initArrayResultArr(Oid fc_array_type, Oid fc_element_type, MemoryContext fc_rcontext,
				   bool fc_subcontext)
{
	ArrayBuildStateArr *fc_astate;
	MemoryContext fc_arr_context = fc_rcontext;	/* 默认使用父上下文 */

	/* 查找元素类型，除非已提供 element_type */
	if (!OidIsValid(fc_element_type))
	{
		fc_element_type = get_element_type(fc_array_type);

		if (!OidIsValid(fc_element_type))
			ereport(ERROR,
					(errcode(ERRCODE_DATATYPE_MISMATCH),
					 errmsg("data type %s is not an array type",
							format_type_be(fc_array_type))));
	}

	/* 创建一个临时上下文来保存所有的垃圾 */
	if (fc_subcontext)
		fc_arr_context = AllocSetContextCreate(fc_rcontext,
											"accumArrayResultArr",
											ALLOCSET_DEFAULT_SIZES);

	/* 注意我们将所有字段初始化为零 */
	fc_astate = (ArrayBuildStateArr *)
		MemoryContextAllocZero(fc_arr_context, sizeof(ArrayBuildStateArr));
	fc_astate->mcontext = fc_arr_context;
	fc_astate->private_cxt = fc_subcontext;

	/* 保存相关数据类型信息 */
	fc_astate->array_type = fc_array_type;
	fc_astate->element_type = fc_element_type;

	return fc_astate;
}

/*
 * accumArrayResultArr - 为数组结果累积一个（或更多）子数组
 *
 * astate 是工作状态（在第一次调用时可以为 NULL）
 * dvalue/disnull 代表要附加到数组的新子数组
 * array_type 是数组类型（必须是有效的 varlena 数组类型）
 * rcontext 是保存工作状态的地方
 */
ArrayBuildStateArr *
accumArrayResultArr(ArrayBuildStateArr *fc_astate,
					Datum fc_dvalue, bool fc_disnull,
					Oid fc_array_type,
					MemoryContext fc_rcontext)
{
	ArrayType  *fc_arg;
	MemoryContext fc_oldcontext;
	int		   *fc_dims,
			   *fc_lbs,
				fc_ndims,
				fc_nitems,
				fc_ndatabytes;
	char	   *fc_data;
	int			fc_i;

	/*
	 * 我们不允许累积空子数组。另一种合理的定义
	 * 是忽略它们，但想要那样的调用者可以跳过调用
	 * 此功能。
	 */
	if (fc_disnull)
		ereport(ERROR,
				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
				 errmsg("cannot accumulate null arrays")));

	/* 在调用者上下文中解构输入数组 */
	fc_arg = DatumGetArrayTypeP(fc_dvalue);

	if (fc_astate == NULL)
		fc_astate = initArrayResultArr(fc_array_type, InvalidOid, fc_rcontext, true);
	else
		Assert(fc_astate->array_type == fc_array_type);

	fc_oldcontext = MemoryContextSwitchTo(fc_astate->mcontext);

	/* 收集此输入的维度 */
	fc_ndims = ARR_NDIM(fc_arg);
	fc_dims = ARR_DIMS(fc_arg);
	fc_lbs = ARR_LBOUND(fc_arg);
	fc_data = ARR_DATA_PTR(fc_arg);
	fc_nitems = ArrayGetNItems(fc_ndims, fc_dims);
	fc_ndatabytes = ARR_SIZE(fc_arg) - ARR_DATA_OFFSET(fc_arg);

	if (fc_astate->ndims == 0)
	{
		/* 第一个输入；检查/保存维度信息 */

		/* 我们是否允许空输入并仅生成空输出？ */
		if (fc_ndims == 0)
			ereport(ERROR,
					(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
					 errmsg("cannot accumulate empty arrays")));
		if (fc_ndims + 1 > MAXDIM)
			ereport(ERROR,
					(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
					 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
							fc_ndims + 1, MAXDIM)));

		/*
		 * 输出数组将具有 n+1 维度，后面的维度与
		 * 输入的维度相匹配。
		 */
		fc_astate->ndims = fc_ndims + 1;
		fc_astate->dims[0] = 0;
		memcpy(&fc_astate->dims[1], fc_dims, fc_ndims * sizeof(int));
		fc_astate->lbs[0] = 1;
		memcpy(&fc_astate->lbs[1], fc_lbs, fc_ndims * sizeof(int));

		/* 至少分配足够的数据空间给此项 */
		fc_astate->abytes = pg_nextpower2_32(Max(1024, fc_ndatabytes + 1));
		fc_astate->data = (char *) palloc(fc_astate->abytes);
	}
	else
	{
		/* 第二个或后续输入：必须与第一个输入的维度匹配 */
		if (fc_astate->ndims != fc_ndims + 1)
			ereport(ERROR,
					(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
					 errmsg("cannot accumulate arrays of different dimensionality")));
		for (fc_i = 0; fc_i < fc_ndims; fc_i++)
		{
			if (fc_astate->dims[fc_i + 1] != fc_dims[fc_i] || fc_astate->lbs[fc_i + 1] != fc_lbs[fc_i])
				ereport(ERROR,
						(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
						 errmsg("cannot accumulate arrays of different dimensionality")));
		}

		/* 如有需要，扩大数据空间 */
		if (fc_astate->nbytes + fc_ndatabytes > fc_astate->abytes)
		{
			fc_astate->abytes = Max(fc_astate->abytes * 2,
								 fc_astate->nbytes + fc_ndatabytes);
			fc_astate->data = (char *) repalloc(fc_astate->data, fc_astate->abytes);
		}
	}

	/*
	 * 复制子数组的数据部分。注意我们假设
	 * 子数组的声明数据长度是正确对齐的。我们不
	 * 必须担心解构元素，因为子数组中的内容应该已经可以。
	 */
	memcpy(fc_astate->data + fc_astate->nbytes, fc_data, fc_ndatabytes);
	fc_astate->nbytes += fc_ndatabytes;

	/* 如有需要，处理空位图 */
	if (fc_astate->nullbitmap || ARR_HASNULL(fc_arg))
	{
		int			fc_newnitems = fc_astate->nitems + fc_nitems;

		if (fc_astate->nullbitmap == NULL)
		{
			/*
			 * 第一个输入包含空值；我们必须追溯性地处理任何
			 * 以前的输入，将所有项目标记为非空。
			 */
			fc_astate->aitems = pg_nextpower2_32(Max(256, fc_newnitems + 1));
			fc_astate->nullbitmap = (bits8 *) palloc((fc_astate->aitems + 7) / 8);
			array_bitmap_copy(fc_astate->nullbitmap, 0,
							  NULL, 0,
							  fc_astate->nitems);
		}
		else if (fc_newnitems > fc_astate->aitems)
		{
			fc_astate->aitems = Max(fc_astate->aitems * 2, fc_newnitems);
			fc_astate->nullbitmap = (bits8 *)
				repalloc(fc_astate->nullbitmap, (fc_astate->aitems + 7) / 8);
		}
		array_bitmap_copy(fc_astate->nullbitmap, fc_astate->nitems,
						  ARR_NULLBITMAP(fc_arg), 0,
						  fc_nitems);
	}

	fc_astate->nitems += fc_nitems;
	fc_astate->dims[0] += 1;

	MemoryContextSwitchTo(fc_oldcontext);

	/* 如果有，释放解构的副本 */
	if ((Pointer) fc_arg != DatumGetPointer(fc_dvalue))
		pfree(fc_arg);

	return fc_astate;
}

/*
 * makeArrayResultArr - 生成 N+1-D 最终结果 accumArrayResultArr
 *
 *	astate 是工作状态（必须不为 NULL）
 *	rcontext 是构建结果的地方
 *	release 为 true 时表示可以释放工作状态
 */
Datum makeArrayResultArr(ArrayBuildStateArr *fc_astate,
				   MemoryContext fc_rcontext,
				   bool fc_release)
{
	ArrayType  *fc_result;
	MemoryContext fc_oldcontext;

	/* 在 rcontext 中构建最终数组结果 */
	fc_oldcontext = MemoryContextSwitchTo(fc_rcontext);

	if (fc_astate->ndims == 0)
	{
		/* 无输入，返回空数组 */
		fc_result = construct_empty_array(fc_astate->element_type);
	}
	else
	{
		int			fc_dataoffset,
					fc_nbytes;

		/* 检查数组维度的溢出 */
		(void) ArrayGetNItems(fc_astate->ndims, fc_astate->dims);
		ArrayCheckBounds(fc_astate->ndims, fc_astate->dims, fc_astate->lbs);

		/* 计算所需的空间 */
		fc_nbytes = fc_astate->nbytes;
		if (fc_astate->nullbitmap != NULL)
		{
			fc_dataoffset = ARR_OVERHEAD_WITHNULLS(fc_astate->ndims, fc_astate->nitems);
			fc_nbytes += fc_dataoffset;
		}
		else
		{
			fc_dataoffset = 0;
			fc_nbytes += ARR_OVERHEAD_NONULLS(fc_astate->ndims);
		}

		fc_result = (ArrayType *) palloc0(fc_nbytes);
		SET_VARSIZE(fc_result, fc_nbytes);
		fc_result->ndim = fc_astate->ndims;
		fc_result->dataoffset = fc_dataoffset;
		fc_result->elemtype = fc_astate->element_type;

		memcpy(ARR_DIMS(fc_result), fc_astate->dims, fc_astate->ndims * sizeof(int));
		memcpy(ARR_LBOUND(fc_result), fc_astate->lbs, fc_astate->ndims * sizeof(int));
		memcpy(ARR_DATA_PTR(fc_result), fc_astate->data, fc_astate->nbytes);

		if (fc_astate->nullbitmap != NULL)
			array_bitmap_copy(ARR_NULLBITMAP(fc_result), 0,
							  fc_astate->nullbitmap, 0,
							  fc_astate->nitems);
	}

	MemoryContextSwitchTo(fc_oldcontext);

	/* 清理所有的垃圾 */
	if (fc_release)
	{
		Assert(fc_astate->private_cxt);
		MemoryContextDelete(fc_astate->mcontext);
	}

	return PointerGetDatum(fc_result);
}

/*
 * 以下三个函数提供与 initArrayResult/accumArrayResult/makeArrayResult 实质上相同的 API，
 * 但可以接受标量或数组输入，调用上述适当的一组函数。
 */

/*
 * initArrayResultAny - 初始化一个空的 ArrayBuildStateAny
 *
 *	input_type 是输入数据类型（可以是元素类型或数组类型）
 *	rcontext 是存放工作状态的地方
 *	subcontext 是一个标志，用于确定是否使用单独的内存上下文
 */
ArrayBuildStateAny *
initArrayResultAny(Oid fc_input_type, MemoryContext fc_rcontext, bool fc_subcontext)
{
	ArrayBuildStateAny *fc_astate;
	Oid			fc_element_type = get_element_type(fc_input_type);

	if (OidIsValid(fc_element_type))
	{
		/* 数组情况 */
		ArrayBuildStateArr *fc_arraystate;

		fc_arraystate = initArrayResultArr(fc_input_type, InvalidOid, fc_rcontext, fc_subcontext);
		fc_astate = (ArrayBuildStateAny *)
			MemoryContextAlloc(fc_arraystate->mcontext,
							   sizeof(ArrayBuildStateAny));
		fc_astate->scalarstate = NULL;
		fc_astate->arraystate = fc_arraystate;
	}
	else
	{
		/* 标量情况 */
		ArrayBuildState *fc_scalarstate;

		/* 让我们检查一下是否有可以放入数组的类型 */
		Assert(OidIsValid(get_array_type(fc_input_type)));

		fc_scalarstate = initArrayResult(fc_input_type, fc_rcontext, fc_subcontext);
		fc_astate = (ArrayBuildStateAny *)
			MemoryContextAlloc(fc_scalarstate->mcontext,
							   sizeof(ArrayBuildStateAny));
		fc_astate->scalarstate = fc_scalarstate;
		fc_astate->arraystate = NULL;
	}

	return fc_astate;
}

/*
 * accumArrayResultAny - 为数组结果累积一个（或多个）输入
 *
 *	astate 是工作状态（在首次调用时可以为 NULL）
 *	dvalue/disnull 表示要附加到数组的新输入
 *	input_type 是输入数据类型（可以是元素类型或数组类型）
 *	rcontext 是存放工作状态的地方
 */
ArrayBuildStateAny *
accumArrayResultAny(ArrayBuildStateAny *fc_astate,
					Datum fc_dvalue, bool fc_disnull,
					Oid fc_input_type,
					MemoryContext fc_rcontext)
{
	if (fc_astate == NULL)
		fc_astate = initArrayResultAny(fc_input_type, fc_rcontext, true);

	if (fc_astate->scalarstate)
		(void) accumArrayResult(fc_astate->scalarstate,
								fc_dvalue, fc_disnull,
								fc_input_type, fc_rcontext);
	else
		(void) accumArrayResultArr(fc_astate->arraystate,
								   fc_dvalue, fc_disnull,
								   fc_input_type, fc_rcontext);

	return fc_astate;
}

/*
 * makeArrayResultAny - 生成 accumArrayResultAny 的最终结果
 *
 *	astate 是工作状态（必须不为 NULL）
 *	rcontext 是构建结果的地方
 *	release 为 true 时表示可以释放工作状态
 */
Datum makeArrayResultAny(ArrayBuildStateAny *fc_astate,
				   MemoryContext fc_rcontext, bool fc_release)
{
	Datum		fc_result;

	if (fc_astate->scalarstate)
	{
		/* 必须使用 makeMdArrayResult 来支持 "release" 参数 */
		int			fc_ndims;
		int			fc_dims[1];
		int			fc_lbs[1];

		/* 如果未提供元素，我们希望创建一个空数组 */
		fc_ndims = (fc_astate->scalarstate->nelems > 0) ? 1 : 0;
		fc_dims[0] = fc_astate->scalarstate->nelems;
		fc_lbs[0] = 1;

		fc_result = makeMdArrayResult(fc_astate->scalarstate, fc_ndims, fc_dims, fc_lbs,
								   fc_rcontext, fc_release);
	}
	else
	{
		fc_result = makeArrayResultArr(fc_astate->arraystate,
									fc_rcontext, fc_release);
	}
	return fc_result;
}


Datum array_larger(PG_FUNCTION_ARGS)
{
	if (fc_array_cmp(fcinfo) > 0)
		PG_RETURN_DATUM(PG_GETARG_DATUM(0));
	else
		PG_RETURN_DATUM(PG_GETARG_DATUM(1));
}

Datum array_smaller(PG_FUNCTION_ARGS)
{
	if (fc_array_cmp(fcinfo) < 0)
		PG_RETURN_DATUM(PG_GETARG_DATUM(0));
	else
		PG_RETURN_DATUM(PG_GETARG_DATUM(1));
}


typedef struct generate_subscripts_fctx
{
	int32		lower;
	int32		upper;
	bool		reverse;
} generate_subscripts_fctx;

/*
 * generate_subscripts(array anyarray, dim int [, reverse bool])
 *		返回数组的所有下标，适用于任何维度
 */
Datum generate_subscripts(PG_FUNCTION_ARGS)
{
	FuncCallContext *fc_funcctx;
	MemoryContext fc_oldcontext;
	generate_subscripts_fctx *fc_fctx;

	/* 仅在函数的第一次调用时完成的工作 */
	if (SRF_IS_FIRSTCALL())
	{
		AnyArrayType *fc_v = PG_GETARG_ANY_ARRAY_P(0);
		int			fc_reqdim = PG_GETARG_INT32(1);
		int		   *fc_lb,
				   *fc_dimv;

		/* 为跨调用持久性创建一个函数上下文 */
		fc_funcctx = SRF_FIRSTCALL_INIT();

		/* 精度检查：看起来真的是一个数组吗？ */
		if (AARR_NDIM(fc_v) <= 0 || AARR_NDIM(fc_v) > MAXDIM)
			SRF_RETURN_DONE(fc_funcctx);

		/* 合理性检查：请求的维度是否有效 */
		if (fc_reqdim <= 0 || fc_reqdim > AARR_NDIM(fc_v))
			SRF_RETURN_DONE(fc_funcctx);

		/*
		 * 切换到适合多次函数调用的内存上下文
		 */
		fc_oldcontext = MemoryContextSwitchTo(fc_funcctx->multi_call_memory_ctx);
		fc_fctx = (generate_subscripts_fctx *) palloc(sizeof(generate_subscripts_fctx));

		fc_lb = AARR_LBOUND(fc_v);
		fc_dimv = AARR_DIMS(fc_v);

		fc_fctx->lower = fc_lb[fc_reqdim - 1];
		fc_fctx->upper = fc_dimv[fc_reqdim - 1] + fc_lb[fc_reqdim - 1] - 1;
		fc_fctx->reverse = (PG_NARGS() < 3) ? false : PG_GETARG_BOOL(2);

		fc_funcctx->user_fctx = fc_fctx;

		MemoryContextSwitchTo(fc_oldcontext);
	}

	fc_funcctx = SRF_PERCALL_SETUP();

	fc_fctx = fc_funcctx->user_fctx;

	if (fc_fctx->lower <= fc_fctx->upper)
	{
		if (!fc_fctx->reverse)
			SRF_RETURN_NEXT(fc_funcctx, Int32GetDatum(fc_fctx->lower++));
		else
			SRF_RETURN_NEXT(fc_funcctx, Int32GetDatum(fc_fctx->upper--));
	}
	else
		/* 当没有更多元素时完成 */
		SRF_RETURN_DONE(fc_funcctx);
}

/*
 * generate_subscripts_nodir
 *		实现 generate_subscripts 的两个参数版本
 */
Datum generate_subscripts_nodir(PG_FUNCTION_ARGS)
{
	/* 只需调用另一个 -- 它可以处理两种情况 */
	return generate_subscripts(fcinfo);
}

/*
 * array_fill_with_lower_bounds
 *		创建并填充具有定义下限的数组。
 */
Datum array_fill_with_lower_bounds(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_dims;
	ArrayType  *fc_lbs;
	ArrayType  *fc_result;
	Oid			fc_elmtype;
	Datum		fc_value;
	bool		fc_isnull;

	if (PG_ARGISNULL(1) || PG_ARGISNULL(2))
		ereport(ERROR,
				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
				 errmsg("dimension array or low bound array cannot be null")));

	fc_dims = PG_GETARG_ARRAYTYPE_P(1);
	fc_lbs = PG_GETARG_ARRAYTYPE_P(2);

	if (!PG_ARGISNULL(0))
	{
		fc_value = PG_GETARG_DATUM(0);
		fc_isnull = false;
	}
	else
	{
		fc_value = 0;
		fc_isnull = true;
	}

	fc_elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
	if (!OidIsValid(fc_elmtype))
		elog(ERROR, "could not determine data type of input");

	fc_result = fc_array_fill_internal(fc_dims, fc_lbs, fc_value, fc_isnull, fc_elmtype, fcinfo);
	PG_RETURN_ARRAYTYPE_P(fc_result);
}

/*
 * array_fill
 *		创建并填充具有默认下限的数组。
 */
Datum array_fill(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_dims;
	ArrayType  *fc_result;
	Oid			fc_elmtype;
	Datum		fc_value;
	bool		fc_isnull;

	if (PG_ARGISNULL(1))
		ereport(ERROR,
				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
				 errmsg("dimension array or low bound array cannot be null")));

	fc_dims = PG_GETARG_ARRAYTYPE_P(1);

	if (!PG_ARGISNULL(0))
	{
		fc_value = PG_GETARG_DATUM(0);
		fc_isnull = false;
	}
	else
	{
		fc_value = 0;
		fc_isnull = true;
	}

	fc_elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
	if (!OidIsValid(fc_elmtype))
		elog(ERROR, "could not determine data type of input");

	fc_result = fc_array_fill_internal(fc_dims, NULL, fc_value, fc_isnull, fc_elmtype, fcinfo);
	PG_RETURN_ARRAYTYPE_P(fc_result);
}

static ArrayType * fc_create_array_envelope(int fc_ndims, int *fc_dimv, int *fc_lbsv, int fc_nbytes,
					  Oid fc_elmtype, int fc_dataoffset)
{
	ArrayType  *fc_result;

	fc_result = (ArrayType *) palloc0(fc_nbytes);
	SET_VARSIZE(fc_result, fc_nbytes);
	fc_result->ndim = fc_ndims;
	fc_result->dataoffset = fc_dataoffset;
	fc_result->elemtype = fc_elmtype;
	memcpy(ARR_DIMS(fc_result), fc_dimv, fc_ndims * sizeof(int));
	memcpy(ARR_LBOUND(fc_result), fc_lbsv, fc_ndims * sizeof(int));

	return fc_result;
}

static ArrayType * fc_array_fill_internal(ArrayType *fc_dims, ArrayType *fc_lbs,
					Datum fc_value, bool fc_isnull, Oid fc_elmtype,
					FunctionCallInfo fcinfo)
{
	ArrayType  *fc_result;
	int		   *fc_dimv;
	int		   *fc_lbsv;
	int			fc_ndims;
	int			fc_nitems;
	int			fc_deflbs[MAXDIM];
	int16		fc_elmlen;
	bool		fc_elmbyval;
	char		fc_elmalign;
	ArrayMetaState *fc_my_extra;

	/*
	 * 参数检查
	 */
	if (ARR_NDIM(fc_dims) > 1)
		ereport(ERROR,
				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
				 errmsg("wrong number of array subscripts"),
				 errdetail("Dimension array must be one dimensional.")));

	if (array_contains_nulls(fc_dims))
		ereport(ERROR,
				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
				 errmsg("dimension values cannot be null")));

	fc_dimv = (int *) ARR_DATA_PTR(fc_dims);
	fc_ndims = (ARR_NDIM(fc_dims) > 0) ? ARR_DIMS(fc_dims)[0] : 0;

	if (fc_ndims < 0)				/* 我们允许零维数组 */
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("invalid number of dimensions: %d", fc_ndims)));
	if (fc_ndims > MAXDIM)
		ereport(ERROR,
				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
				 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
						fc_ndims, MAXDIM)));

	if (fc_lbs != NULL)
	{
		if (ARR_NDIM(fc_lbs) > 1)
			ereport(ERROR,
					(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
					 errmsg("wrong number of array subscripts"),
					 errdetail("Dimension array must be one dimensional.")));

		if (array_contains_nulls(fc_lbs))
			ereport(ERROR,
					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
					 errmsg("dimension values cannot be null")));

		if (fc_ndims != ((ARR_NDIM(fc_lbs) > 0) ? ARR_DIMS(fc_lbs)[0] : 0))
			ereport(ERROR,
					(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
					 errmsg("wrong number of array subscripts"),
					 errdetail("Low bound array has different size than dimensions array.")));

		fc_lbsv = (int *) ARR_DATA_PTR(fc_lbs);
	}
	else
	{
		int			fc_i;

		for (fc_i = 0; fc_i < MAXDIM; fc_i++)
			fc_deflbs[fc_i] = 1;

		fc_lbsv = fc_deflbs;
	}

	/* 这检查数组维度的溢出 */
	fc_nitems = ArrayGetNItems(fc_ndims, fc_dimv);
	ArrayCheckBounds(fc_ndims, fc_dimv, fc_lbsv);

	/* 空数组的快速通道 */
	if (fc_nitems <= 0)
		return construct_empty_array(fc_elmtype);

	/*
	 * 我们安排在一系列调用中只查找元素类型的信息一次，
	 * 假设元素类型在我们面前没有改变。
	 */
	fc_my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
	if (fc_my_extra == NULL)
	{
		fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
													  sizeof(ArrayMetaState));
		fc_my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
		fc_my_extra->element_type = InvalidOid;
	}

	if (fc_my_extra->element_type != fc_elmtype)
	{
		/* 获取元素类型的信息 */
		get_typlenbyvalalign(fc_elmtype,
							 &fc_my_extra->typlen,
							 &fc_my_extra->typbyval,
							 &fc_my_extra->typalign);
		fc_my_extra->element_type = fc_elmtype;
	}

	fc_elmlen = fc_my_extra->typlen;
	fc_elmbyval = fc_my_extra->typbyval;
	fc_elmalign = fc_my_extra->typalign;

	/* 计算所需空间 */
	if (!fc_isnull)
	{
		int			fc_i;
		char	   *fc_p;
		int			fc_nbytes;
		int			fc_totbytes;

		/* 确保数据没有被烘烤 */
		if (fc_elmlen == -1)
			fc_value = PointerGetDatum(PG_DETOAST_DATUM(fc_value));

		fc_nbytes = att_addlength_datum(0, fc_elmlen, fc_value);
		fc_nbytes = att_align_nominal(fc_nbytes, fc_elmalign);
		Assert(fc_nbytes > 0);

		fc_totbytes = fc_nbytes * fc_nitems;

		/* 检查乘法或总请求的溢出 */
		if (fc_totbytes / fc_nbytes != fc_nitems ||
			!AllocSizeIsValid(fc_totbytes))
			ereport(ERROR,
					(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
					 errmsg("array size exceeds the maximum allowed (%d)",
							(int) MaxAllocSize)));

		/*
		 * 该加法不能溢出，但可能导致我们超过
		 * MaxAllocSize。在这种情况下，我们留给 palloc 来抱怨。
		 */
		fc_totbytes += ARR_OVERHEAD_NONULLS(fc_ndims);

		fc_result = fc_create_array_envelope(fc_ndims, fc_dimv, fc_lbsv, fc_totbytes,
									   fc_elmtype, 0);

		fc_p = ARR_DATA_PTR(fc_result);
		for (fc_i = 0; fc_i < fc_nitems; fc_i++)
			fc_p += fc_ArrayCastAndSet(fc_value, fc_elmlen, fc_elmbyval, fc_elmalign, fc_p);
	}
	else
	{
		int			fc_nbytes;
		int			fc_dataoffset;

		fc_dataoffset = ARR_OVERHEAD_WITHNULLS(fc_ndims, fc_nitems);
		fc_nbytes = fc_dataoffset;

		fc_result = fc_create_array_envelope(fc_ndims, fc_dimv, fc_lbsv, fc_nbytes,
									   fc_elmtype, fc_dataoffset);

		/* create_array_envelope 已经将位图置零，所以我们完成了 */
	}

	return fc_result;
}


/*
 * UNNEST
 */
Datum array_unnest(PG_FUNCTION_ARGS)
{
	typedef struct
	{
		array_iter	iter;
		int			nextelem;
		int			numelems;
		int16		elmlen;
		bool		elmbyval;
		char		elmalign;
	} array_unnest_fctx;

	FuncCallContext *fc_funcctx;
	array_unnest_fctx *fc_fctx;
	MemoryContext fc_oldcontext;

	/* 仅在函数的第一次调用时完成的工作 */
	if (SRF_IS_FIRSTCALL())
	{
		AnyArrayType *fc_arr;

		/* 为跨调用持久性创建一个函数上下文 */
		fc_funcctx = SRF_FIRSTCALL_INIT();

		/*
		 * 切换到适合多次函数调用的内存上下文
		 */
		fc_oldcontext = MemoryContextSwitchTo(fc_funcctx->multi_call_memory_ctx);

		/*
		 * 获取数组值并在需要时解压。如果我们必须解压，
		 * 我们希望解压后的副本位于 multi_call_memory_ctx 中，
		 * 以便在完成后消失，而不是之前。（如果没有发生解压，
		 * 我们假设最初传递的数组会持续到那时。）
		 */
		fc_arr = PG_GETARG_ANY_ARRAY_P(0);

		/* 为用户上下文分配内存 */
		fc_fctx = (array_unnest_fctx *) palloc(sizeof(array_unnest_fctx));

		/* 初始化状态 */
		array_iter_setup(&fc_fctx->iter, fc_arr);
		fc_fctx->nextelem = 0;
		fc_fctx->numelems = ArrayGetNItems(AARR_NDIM(fc_arr), AARR_DIMS(fc_arr));

		if (VARATT_IS_EXPANDED_HEADER(fc_arr))
		{
			/* 我们可以直接从扩展的数组中获取类型数据 */
			fc_fctx->elmlen = fc_arr->xpn.typlen;
			fc_fctx->elmbyval = fc_arr->xpn.typbyval;
			fc_fctx->elmalign = fc_arr->xpn.typalign;
		}
		else
			get_typlenbyvalalign(AARR_ELEMTYPE(fc_arr),
								 &fc_fctx->elmlen,
								 &fc_fctx->elmbyval,
								 &fc_fctx->elmalign);

		fc_funcctx->user_fctx = fc_fctx;
		MemoryContextSwitchTo(fc_oldcontext);
	}

	/* 每次调用函数时完成的工作 */
	fc_funcctx = SRF_PERCALL_SETUP();
	fc_fctx = fc_funcctx->user_fctx;

	if (fc_fctx->nextelem < fc_fctx->numelems)
	{
		int			fc_offset = fc_fctx->nextelem++;
		Datum		fc_elem;

		fc_elem = array_iter_next(&fc_fctx->iter, &fcinfo->isnull, fc_offset,
							   fc_fctx->elmlen, fc_fctx->elmbyval, fc_fctx->elmalign);

		SRF_RETURN_NEXT(fc_funcctx, fc_elem);
	}
	else
	{
		/* 当没有更多内容时执行 */
		SRF_RETURN_DONE(fc_funcctx);
	}
}

/*
 * 规划器支持函数用于 array_unnest(anyarray)
 */
Datum array_unnest_support(PG_FUNCTION_ARGS)
{
	Node	   *fc_rawreq = (Node *) PG_GETARG_POINTER(0);
	Node	   *fc_ret = NULL;

	if (IsA(fc_rawreq, SupportRequestRows))
	{
		/* 尝试估算返回的行数 */
		SupportRequestRows *fc_req = (SupportRequestRows *) fc_rawreq;

		if (is_funcclause(fc_req->node))	/* 过于谨慎 */
		{
			List	   *fc_args = ((FuncExpr *) fc_req->node)->args;
			Node	   *fc_arg1;

			/* 在这里我们可以使用估计的参数值 */
			fc_arg1 = estimate_expression_value(fc_req->root, linitial(fc_args));

			fc_req->rows = estimate_array_length(fc_arg1);
			fc_ret = (Node *) fc_req;
		}
	}

	PG_RETURN_POINTER(fc_ret);
}


/*
 * array_replace/array_remove 支持
 *
 * 查找所有与 search/search_isnull 匹配（不区分）的数组条目，
 * 如果 remove 为真则删除它们，否则用 replace/replace_isnull 替换它们。
 * 比较是使用指定的排序规则进行的。fcinfo 仅用于缓存目的。
 */
static ArrayType * fc_array_replace_internal(ArrayType *fc_array,
					   Datum fc_search, bool fc_search_isnull,
					   Datum fc_replace, bool fc_replace_isnull,
					   bool fc_remove, Oid fc_collation,
					   FunctionCallInfo fcinfo)
{
	LOCAL_FCINFO(fc_locfcinfo, 2);
	ArrayType  *fc_result;
	Oid			fc_element_type;
	Datum	   *fc_values;
	bool	   *fc_nulls;
	int		   *fc_dim;
	int			fc_ndim;
	int			fc_nitems,
				fc_nresult;
	int			fc_i;
	int32		fc_nbytes = 0;
	int32		fc_dataoffset;
	bool		fc_hasnulls;
	int			fc_typlen;
	bool		fc_typbyval;
	char		fc_typalign;
	char	   *fc_arraydataptr;
	bits8	   *fc_bitmap;
	int			fc_bitmask;
	bool		fc_changed = false;
	TypeCacheEntry *fc_typentry;

	fc_element_type = ARR_ELEMTYPE(fc_array);
	fc_ndim = ARR_NDIM(fc_array);
	fc_dim = ARR_DIMS(fc_array);
	fc_nitems = ArrayGetNItems(fc_ndim, fc_dim);

	/* 如果输入数组为空，则返回未修改的输入数组 */
	if (fc_nitems <= 0)
		return fc_array;

	/*
	 * 我们无法从多维数组中删除元素，因为结果可能不是矩形的。
	 */
	if (fc_remove && fc_ndim > 1)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("removing elements from multidimensional arrays is not supported")));

	/*
	 * 我们安排每系列调用只查找一次相等函数，假设元素类型在我们下面不变。
	 */
	fc_typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
	if (fc_typentry == NULL ||
		fc_typentry->type_id != fc_element_type)
	{
		fc_typentry = lookup_type_cache(fc_element_type,
									 TYPECACHE_EQ_OPR_FINFO);
		if (!OidIsValid(fc_typentry->eq_opr_finfo.fn_oid))
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_FUNCTION),
					 errmsg("could not identify an equality operator for type %s",
							format_type_be(fc_element_type))));
		fcinfo->flinfo->fn_extra = (void *) fc_typentry;
	}
	fc_typlen = fc_typentry->typlen;
	fc_typbyval = fc_typentry->typbyval;
	fc_typalign = fc_typentry->typalign;

	/*
	 * 如果值被压缩，则先解压缩值。替换值必须解压缩才能插入结果数组，而只解压缩一次搜索值可以节省周期。
	 */
	if (fc_typlen == -1)
	{
		if (!fc_search_isnull)
			fc_search = PointerGetDatum(PG_DETOAST_DATUM(fc_search));
		if (!fc_replace_isnull)
			fc_replace = PointerGetDatum(PG_DETOAST_DATUM(fc_replace));
	}

	/* 准备应用比较操作符 */
	InitFunctionCallInfoData(*fc_locfcinfo, &fc_typentry->eq_opr_finfo, 2,
							 fc_collation, NULL, NULL);

	/* 为新值分配临时数组 */
	fc_values = (Datum *) palloc(fc_nitems * sizeof(Datum));
	fc_nulls = (bool *) palloc(fc_nitems * sizeof(bool));

	/* 循环遍历源数据 */
	fc_arraydataptr = ARR_DATA_PTR(fc_array);
	fc_bitmap = ARR_NULLBITMAP(fc_array);
	fc_bitmask = 1;
	fc_hasnulls = false;
	fc_nresult = 0;

	for (fc_i = 0; fc_i < fc_nitems; fc_i++)
	{
		Datum		fc_elt;
		bool		fc_isNull;
		bool		fc_oprresult;
		bool		fc_skip = false;

		/* 获取源元素，检查是否为 NULL */
		if (fc_bitmap && (*fc_bitmap & fc_bitmask) == 0)
		{
			fc_isNull = true;
			/* 如果搜索 NULL，我们就匹配上了 */
			if (fc_search_isnull)
			{
				if (fc_remove)
				{
					fc_skip = true;
					fc_changed = true;
				}
				else if (!fc_replace_isnull)
				{
					fc_values[fc_nresult] = fc_replace;
					fc_isNull = false;
					fc_changed = true;
				}
			}
		}
		else
		{
			fc_isNull = false;
			fc_elt = fetch_att(fc_arraydataptr, fc_typbyval, fc_typlen);
			fc_arraydataptr = att_addlength_datum(fc_arraydataptr, fc_typlen, fc_elt);
			fc_arraydataptr = (char *) att_align_nominal(fc_arraydataptr, fc_typalign);

			if (fc_search_isnull)
			{
				/* 无法匹配，保持元素 */
				fc_values[fc_nresult] = fc_elt;
			}
			else
			{
				/*
				 * 对元素对应用操作符；将 NULL 视为 false
				 */
				fc_locfcinfo->args[0].value = fc_elt;
				fc_locfcinfo->args[0].isnull = false;
				fc_locfcinfo->args[1].value = fc_search;
				fc_locfcinfo->args[1].isnull = false;
				fc_locfcinfo->isnull = false;
				fc_oprresult = DatumGetBool(FunctionCallInvoke(fc_locfcinfo));
				if (fc_locfcinfo->isnull || !fc_oprresult)
				{
					/* 无匹配，保持元素 */
					fc_values[fc_nresult] = fc_elt;
				}
				else
				{
					/* 匹配，所以替换或删除 */
					fc_changed = true;
					if (fc_remove)
						fc_skip = true;
					else
					{
						fc_values[fc_nresult] = fc_replace;
						fc_isNull = fc_replace_isnull;
					}
				}
			}
		}

		if (!fc_skip)
		{
			fc_nulls[fc_nresult] = fc_isNull;
			if (fc_isNull)
				fc_hasnulls = true;
			else
			{
				/* 更新总结果大小 */
				fc_nbytes = att_addlength_datum(fc_nbytes, fc_typlen, fc_values[fc_nresult]);
				fc_nbytes = att_align_nominal(fc_nbytes, fc_typalign);
				/* 检查总请求的溢出 */
				if (!AllocSizeIsValid(fc_nbytes))
					ereport(ERROR,
							(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
							 errmsg("array size exceeds the maximum allowed (%d)",
									(int) MaxAllocSize)));
			}
			fc_nresult++;
		}

		/* 如果有，向前推进位图指针 */
		if (fc_bitmap)
		{
			fc_bitmask <<= 1;
			if (fc_bitmask == 0x100)
			{
				fc_bitmap++;
				fc_bitmask = 1;
			}
		}
	}

	/*
	 * 如果没有变化，只需返回原始数组
	 */
	if (!fc_changed)
	{
		pfree(fc_values);
		pfree(fc_nulls);
		return fc_array;
	}

	/* 如果所有元素都被移除，返回一个空数组 */
	if (fc_nresult == 0)
	{
		pfree(fc_values);
		pfree(fc_nulls);
		return construct_empty_array(fc_element_type);
	}

	/* 分配并初始化结果数组 */
	if (fc_hasnulls)
	{
		fc_dataoffset = ARR_OVERHEAD_WITHNULLS(fc_ndim, fc_nresult);
		fc_nbytes += fc_dataoffset;
	}
	else
	{
		fc_dataoffset = 0;			/* 表示没有空位图的标记 */
		fc_nbytes += ARR_OVERHEAD_NONULLS(fc_ndim);
	}
	fc_result = (ArrayType *) palloc0(fc_nbytes);
	SET_VARSIZE(fc_result, fc_nbytes);
	fc_result->ndim = fc_ndim;
	fc_result->dataoffset = fc_dataoffset;
	fc_result->elemtype = fc_element_type;
	memcpy(ARR_DIMS(fc_result), ARR_DIMS(fc_array), fc_ndim * sizeof(int));
	memcpy(ARR_LBOUND(fc_result), ARR_LBOUND(fc_array), fc_ndim * sizeof(int));

	if (fc_remove)
	{
		/* 调整结果长度 */
		ARR_DIMS(fc_result)[0] = fc_nresult;
	}

	/* 将数据插入结果数组 */
	CopyArrayEls(fc_result,
				 fc_values, fc_nulls, fc_nresult,
				 fc_typlen, fc_typbyval, fc_typalign,
				 false);

	pfree(fc_values);
	pfree(fc_nulls);

	return fc_result;
}

/*
 * 从数组中删除元素的任何出现
 *
 * 如果在多维数组上使用，这将引发错误。
 */
Datum array_remove(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_array;
	Datum		fc_search = PG_GETARG_DATUM(1);
	bool		fc_search_isnull = PG_ARGISNULL(1);

	if (PG_ARGISNULL(0))
		PG_RETURN_NULL();
	fc_array = PG_GETARG_ARRAYTYPE_P(0);

	fc_array = fc_array_replace_internal(fc_array,
								   fc_search, fc_search_isnull,
								   (Datum) 0, true,
								   true, PG_GET_COLLATION(),
								   fcinfo);
	PG_RETURN_ARRAYTYPE_P(fc_array);
}

/*
 * 替换数组中元素的任何出现
 */
Datum array_replace(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_array;
	Datum		fc_search = PG_GETARG_DATUM(1);
	bool		fc_search_isnull = PG_ARGISNULL(1);
	Datum		fc_replace = PG_GETARG_DATUM(2);
	bool		fc_replace_isnull = PG_ARGISNULL(2);

	if (PG_ARGISNULL(0))
		PG_RETURN_NULL();
	fc_array = PG_GETARG_ARRAYTYPE_P(0);

	fc_array = fc_array_replace_internal(fc_array,
								   fc_search, fc_search_isnull,
								   fc_replace, fc_replace_isnull,
								   false, PG_GET_COLLATION(),
								   fcinfo);
	PG_RETURN_ARRAYTYPE_P(fc_array);
}

/*
 * 实现 width_bucket(anyelement, anyarray)。
 *
 * 'thresholds' 是一个包含每个桶的下限值的数组；
 * 这些值必须按从小到大的顺序排序，否则会产生错误的结果。
 * 如果提供了 N 个阈值，输出范围是从 0 到 N：
 * 0 是对于输入 < 第一个阈值，N 是对于输入 >= 最后一个阈值。
 */
Datum width_bucket_array(PG_FUNCTION_ARGS)
{
	Datum		fc_operand = PG_GETARG_DATUM(0);
	ArrayType  *fc_thresholds = PG_GETARG_ARRAYTYPE_P(1);
	Oid			fc_collation = PG_GET_COLLATION();
	Oid			fc_element_type = ARR_ELEMTYPE(fc_thresholds);
	int			fc_result;

	/* 检查输入 */
	if (ARR_NDIM(fc_thresholds) > 1)
		ereport(ERROR,
				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
				 errmsg("thresholds must be one-dimensional array")));

	if (array_contains_nulls(fc_thresholds))
		ereport(ERROR,
				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
				 errmsg("thresholds array must not contain NULLs")));

	/* 我们有一个专门用于 float8 数据的实现 */
	if (fc_element_type == FLOAT8OID)
		fc_result = fc_width_bucket_array_float8(fc_operand, fc_thresholds);
	else
	{
		TypeCacheEntry *fc_typentry;

		/* 缓存关于输入类型的信息 */
		fc_typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
		if (fc_typentry == NULL ||
			fc_typentry->type_id != fc_element_type)
		{
			fc_typentry = lookup_type_cache(fc_element_type,
										 TYPECACHE_CMP_PROC_FINFO);
			if (!OidIsValid(fc_typentry->cmp_proc_finfo.fn_oid))
				ereport(ERROR,
						(errcode(ERRCODE_UNDEFINED_FUNCTION),
						 errmsg("could not identify a comparison function for type %s",
								format_type_be(fc_element_type))));
			fcinfo->flinfo->fn_extra = (void *) fc_typentry;
		}

		/*
		 * 对于固定和可变宽度类型我们有不同的实现路径，
		 * 因为在第一种情况下索引数组的成本要便宜得多。
		 */
		if (fc_typentry->typlen > 0)
			fc_result = fc_width_bucket_array_fixed(fc_operand, fc_thresholds,
											  fc_collation, fc_typentry);
		else
			fc_result = fc_width_bucket_array_variable(fc_operand, fc_thresholds,
												 fc_collation, fc_typentry);
	}

	/* 避免在处理已烤制输入时泄漏内存。 */
	PG_FREE_IF_COPY(fc_thresholds, 1);

	PG_RETURN_INT32(fc_result);
}

/*
 * float8 数据的 width_bucket_array。
 */
static int fc_width_bucket_array_float8(Datum fc_operand, ArrayType *fc_thresholds)
{
	float8		fc_op = DatumGetFloat8(fc_operand);
	float8	   *fc_thresholds_data;
	int			fc_left;
	int			fc_right;

	/*
	 * 由于我们知道数组中不包含 NULL，因此可以直接索引它。
	 */
	fc_thresholds_data = (float8 *) ARR_DATA_PTR(fc_thresholds);

	fc_left = 0;
	fc_right = ArrayGetNItems(ARR_NDIM(fc_thresholds), ARR_DIMS(fc_thresholds));

	/*
	 * 如果探测值是 NaN，则它大于或等于所有可能的阈值（包括其他 NaN），因此我们无需搜索。
	 * 注意，即使数组中包含多个 NaN（只要它们正确排序），这也会产生与搜索相同的结果，
	 * 因为循环逻辑将找到多个相等阈值中最右边的值。
	 */
	if (isnan(fc_op))
		return fc_right;

	/* 查找桶 */
	while (fc_left < fc_right)
	{
		int			fc_mid = (fc_left + fc_right) / 2;

		if (isnan(fc_thresholds_data[fc_mid]) || fc_op < fc_thresholds_data[fc_mid])
			fc_right = fc_mid;
		else
			fc_left = fc_mid + 1;
	}

	return fc_left;
}

/*
 * width_bucket_array 用于通用固定宽度数据类型。
 */
static int fc_width_bucket_array_fixed(Datum fc_operand,
						 ArrayType *fc_thresholds,
						 Oid fc_collation,
						 TypeCacheEntry *fc_typentry)
{
	LOCAL_FCINFO(fc_locfcinfo, 2);
	char	   *fc_thresholds_data;
	int			fc_typlen = fc_typentry->typlen;
	bool		fc_typbyval = fc_typentry->typbyval;
	int			fc_left;
	int			fc_right;

	/*
	 * 由于我们知道数组中不包含 NULL，因此可以直接索引它。
	 */
	fc_thresholds_data = (char *) ARR_DATA_PTR(fc_thresholds);

	InitFunctionCallInfoData(*fc_locfcinfo, &fc_typentry->cmp_proc_finfo, 2,
							 fc_collation, NULL, NULL);

	/* 查找桶 */
	fc_left = 0;
	fc_right = ArrayGetNItems(ARR_NDIM(fc_thresholds), ARR_DIMS(fc_thresholds));
	while (fc_left < fc_right)
	{
		int			fc_mid = (fc_left + fc_right) / 2;
		char	   *fc_ptr;
		int32		fc_cmpresult;

		fc_ptr = fc_thresholds_data + fc_mid * fc_typlen;

		fc_locfcinfo->args[0].value = fc_operand;
		fc_locfcinfo->args[0].isnull = false;
		fc_locfcinfo->args[1].value = fetch_att(fc_ptr, fc_typbyval, fc_typlen);
		fc_locfcinfo->args[1].isnull = false;

		fc_cmpresult = DatumGetInt32(FunctionCallInvoke(fc_locfcinfo));

		/* 我们不期望比较支持函数返回 NULL */
		Assert(!fc_locfcinfo->isnull);

		if (fc_cmpresult < 0)
			fc_right = fc_mid;
		else
			fc_left = fc_mid + 1;
	}

	return fc_left;
}

/*
 * width_bucket_array 用于通用可变宽度数据类型。
 */
static int fc_width_bucket_array_variable(Datum fc_operand,
							ArrayType *fc_thresholds,
							Oid fc_collation,
							TypeCacheEntry *fc_typentry)
{
	LOCAL_FCINFO(fc_locfcinfo, 2);
	char	   *fc_thresholds_data;
	int			fc_typlen = fc_typentry->typlen;
	bool		fc_typbyval = fc_typentry->typbyval;
	char		fc_typalign = fc_typentry->typalign;
	int			fc_left;
	int			fc_right;

	fc_thresholds_data = (char *) ARR_DATA_PTR(fc_thresholds);

	InitFunctionCallInfoData(*fc_locfcinfo, &fc_typentry->cmp_proc_finfo, 2,
							 fc_collation, NULL, NULL);

	/* 查找桶 */
	fc_left = 0;
	fc_right = ArrayGetNItems(ARR_NDIM(fc_thresholds), ARR_DIMS(fc_thresholds));
	while (fc_left < fc_right)
	{
		int			fc_mid = (fc_left + fc_right) / 2;
		char	   *fc_ptr;
		int			fc_i;
		int32		fc_cmpresult;

		/* 通过从左侧元素前进找到中间元素 */
		fc_ptr = fc_thresholds_data;
		for (fc_i = fc_left; fc_i < fc_mid; fc_i++)
		{
			fc_ptr = att_addlength_pointer(fc_ptr, fc_typlen, fc_ptr);
			fc_ptr = (char *) att_align_nominal(fc_ptr, fc_typalign);
		}

		fc_locfcinfo->args[0].value = fc_operand;
		fc_locfcinfo->args[0].isnull = false;
		fc_locfcinfo->args[1].value = fetch_att(fc_ptr, fc_typbyval, fc_typlen);
		fc_locfcinfo->args[1].isnull = false;

		fc_cmpresult = DatumGetInt32(FunctionCallInvoke(fc_locfcinfo));

		/* 我们不期望比较支持函数返回 NULL */
		Assert(!fc_locfcinfo->isnull);

		if (fc_cmpresult < 0)
			fc_right = fc_mid;
		else
		{
			fc_left = fc_mid + 1;

			/*
			 * 移动阈值指针以匹配新的“左”索引，这样我们就
			 * 不必再次搜索这些元素。这个技巧
			 * 确保我们只进行 O(N) 的数组索引工作，而不是 O(N^2)。
			 */
			fc_ptr = att_addlength_pointer(fc_ptr, fc_typlen, fc_ptr);
			fc_thresholds_data = (char *) att_align_nominal(fc_ptr, fc_typalign);
		}
	}

	return fc_left;
}

/*
 * 通过构建适当的切片修剪数组的最后 N 个元素。
 * 仅修剪第一维。
 */
Datum trim_array(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_v = PG_GETARG_ARRAYTYPE_P(0);
	int			fc_n = PG_GETARG_INT32(1);
	int			fc_array_length = (ARR_NDIM(fc_v) > 0) ? ARR_DIMS(fc_v)[0] : 0;
	int16		fc_elmlen;
	bool		fc_elmbyval;
	char		fc_elmalign;
	int			fc_lower[MAXDIM];
	int			fc_upper[MAXDIM];
	bool		fc_lowerProvided[MAXDIM];
	bool		fc_upperProvided[MAXDIM];
	Datum		fc_result;

	/* 按规范，如果越界则抛出错误 */
	if (fc_n < 0 || fc_n > fc_array_length)
		ereport(ERROR,
				(errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
				 errmsg("number of elements to trim must be between 0 and %d",
						fc_array_length)));

	/* 除了第一个上界外，所有界限都设置为未提供 */
	memset(fc_lowerProvided, false, sizeof(fc_lowerProvided));
	memset(fc_upperProvided, false, sizeof(fc_upperProvided));
	if (ARR_NDIM(fc_v) > 0)
	{
		fc_upper[0] = ARR_LBOUND(fc_v)[0] + fc_array_length - fc_n - 1;
		fc_upperProvided[0] = true;
	}

	/* 获取关于元素类型所需的信息 */
	get_typlenbyvalalign(ARR_ELEMTYPE(fc_v), &fc_elmlen, &fc_elmbyval, &fc_elmalign);

	/* 获取切片 */
	fc_result = array_get_slice(PointerGetDatum(fc_v), 1,
							 fc_upper, fc_lower, fc_upperProvided, fc_lowerProvided,
							 -1, fc_elmlen, fc_elmbyval, fc_elmalign);

	PG_RETURN_DATUM(fc_result);
}
